Merge "Compile android-non-updatable separately"
diff --git a/config/OWNERS b/config/OWNERS
index 001038d..0691dbc 100644
--- a/config/OWNERS
+++ b/config/OWNERS
@@ -1,14 +1,8 @@
include /ZYGOTE_OWNERS
-# compat-team@ for changes to hiddenapi files
-
-per-file hiddenapi-* = andreionea@google.com, mathewi@google.com, satayev@google.com
-
# art-team@ manages the boot image profiles
per-file boot-* = calin@google.com, mathieuc@google.com, ngeoffray@google.com
per-file dirty-image-objects = calin@google.com, mathieuc@google.com, ngeoffray@google.com
per-file generate-preloaded-classes.sh = calin@google.com, mathieuc@google.com, ngeoffray@google.com
per-file preloaded-classes* = calin@google.com, mathieuc@google.com, ngeoffray@google.com
-# Escalations:
-per-file hiddenapi-* = bdc@google.com, narayan@google.com
diff --git a/core/api/current.txt b/core/api/current.txt
index 5a2b8bd..cc425f0 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -29284,7 +29284,6 @@
field public static final String HARDWARE;
field public static final String HOST;
field public static final String ID;
- field public static final boolean IS_DEBUGGABLE;
field public static final String MANUFACTURER;
field public static final String MODEL;
field @NonNull public static final String ODM_SKU;
diff --git a/core/api/module-lib-current.txt b/core/api/module-lib-current.txt
index e9dba8e..e7a7fd2 100644
--- a/core/api/module-lib-current.txt
+++ b/core/api/module-lib-current.txt
@@ -117,6 +117,10 @@
method public final void markVintfStability();
}
+ public class Build {
+ method public static boolean isDebuggable();
+ }
+
public static class Build.VERSION {
field public static final int FIRST_SDK_INT;
}
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index f31cd63..c18a9e3 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -6808,14 +6808,14 @@
method @NonNull @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public android.os.connectivity.WifiBatteryStats getWifiBatteryStats();
method @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public void reportFullWifiLockAcquiredFromSource(@NonNull android.os.WorkSource);
method @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public void reportFullWifiLockReleasedFromSource(@NonNull android.os.WorkSource);
- method @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public void reportMobileRadioPowerState(boolean, int) throws java.lang.RuntimeException;
+ method @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public void reportMobileRadioPowerState(boolean, int);
method @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public void reportWifiBatchedScanStartedFromSource(@NonNull android.os.WorkSource, @IntRange(from=0) int);
method @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public void reportWifiBatchedScanStoppedFromSource(@NonNull android.os.WorkSource);
method @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public void reportWifiMulticastDisabled(@NonNull android.os.WorkSource);
method @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public void reportWifiMulticastEnabled(@NonNull android.os.WorkSource);
method @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public void reportWifiOff();
method @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public void reportWifiOn();
- method @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public void reportWifiRadioPowerState(boolean, int) throws java.lang.RuntimeException;
+ method @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public void reportWifiRadioPowerState(boolean, int);
method @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public void reportWifiRssiChanged(@IntRange(from=0xffffff81, to=0) int);
method @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public void reportWifiScanStartedFromSource(@NonNull android.os.WorkSource);
method @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public void reportWifiScanStoppedFromSource(@NonNull android.os.WorkSource);
@@ -7183,10 +7183,15 @@
method @RequiresPermission(android.Manifest.permission.RECOVERY) public static void processPackage(android.content.Context, java.io.File, android.os.RecoverySystem.ProgressListener, android.os.Handler) throws java.io.IOException;
method @RequiresPermission(android.Manifest.permission.RECOVERY) public static void processPackage(android.content.Context, java.io.File, android.os.RecoverySystem.ProgressListener) throws java.io.IOException;
method @Deprecated @RequiresPermission(android.Manifest.permission.RECOVERY) public static void rebootAndApply(@NonNull android.content.Context, @NonNull String, @NonNull String) throws java.io.IOException;
- method @RequiresPermission(anyOf={android.Manifest.permission.RECOVERY, android.Manifest.permission.REBOOT}) public static void rebootAndApply(@NonNull android.content.Context, @NonNull String, boolean) throws java.io.IOException;
+ method @RequiresPermission(anyOf={android.Manifest.permission.RECOVERY, android.Manifest.permission.REBOOT}) public static int rebootAndApply(@NonNull android.content.Context, @NonNull String, boolean) throws java.io.IOException;
method @RequiresPermission(allOf={android.Manifest.permission.RECOVERY, android.Manifest.permission.REBOOT}) public static void rebootWipeAb(android.content.Context, java.io.File, String) throws java.io.IOException;
method @RequiresPermission(android.Manifest.permission.RECOVERY) public static void scheduleUpdateOnBoot(android.content.Context, java.io.File) throws java.io.IOException;
method public static boolean verifyPackageCompatibility(java.io.File) throws java.io.IOException;
+ field public static final int RESUME_ON_REBOOT_REBOOT_ERROR_INVALID_PACKAGE_NAME = 2000; // 0x7d0
+ field public static final int RESUME_ON_REBOOT_REBOOT_ERROR_LSKF_NOT_CAPTURED = 3000; // 0xbb8
+ field public static final int RESUME_ON_REBOOT_REBOOT_ERROR_PROVIDER_PREPARATION_FAILURE = 5000; // 0x1388
+ field public static final int RESUME_ON_REBOOT_REBOOT_ERROR_SLOT_MISMATCH = 4000; // 0xfa0
+ field public static final int RESUME_ON_REBOOT_REBOOT_ERROR_UNSPECIFIED = 1000; // 0x3e8
}
public final class RemoteCallback implements android.os.Parcelable {
@@ -8123,7 +8128,7 @@
}
public final class KeyGenParameterSpec implements java.security.spec.AlgorithmParameterSpec {
- method @Nullable public int[] getAttestationIds();
+ method @NonNull public int[] getAttestationIds();
method public int getNamespace();
}
@@ -11890,7 +11895,8 @@
}
public interface SipDelegateConnection {
- method public void closeDialog(@NonNull String);
+ method public default void cleanupSession(@NonNull String);
+ method @Deprecated public default void closeDialog(@NonNull String);
method public void notifyMessageReceiveError(@NonNull String, int);
method public void notifyMessageReceived(@NonNull String);
method public void sendMessage(@NonNull android.telephony.ims.SipMessage, long);
@@ -12340,7 +12346,8 @@
}
public interface SipDelegate {
- method public void closeDialog(@NonNull String);
+ method public default void cleanupSession(@NonNull String);
+ method @Deprecated public default void closeDialog(@NonNull String);
method public void notifyMessageReceiveError(@NonNull String, int);
method public void notifyMessageReceived(@NonNull String);
method public void sendMessage(@NonNull android.telephony.ims.SipMessage, long);
@@ -12568,7 +12575,7 @@
method public void onStartFailed(int, @NonNull android.os.PersistableBundle);
method public void onStarted(@NonNull android.os.PersistableBundle);
method public void onStopFailed(int, @NonNull android.os.PersistableBundle);
- method public void onStopped();
+ method public void onStopped(int, @NonNull android.os.PersistableBundle);
field public static final int REASON_BAD_PARAMETERS = 3; // 0x3
field public static final int REASON_GENERIC_ERROR = 4; // 0x4
field public static final int REASON_LOCAL_REQUEST = 1; // 0x1
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 0c57f4e..f7b101d 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -1022,6 +1022,7 @@
public class Build {
method public static boolean is64BitAbi(String);
+ method public static boolean isDebuggable();
field public static final boolean IS_EMULATOR;
}
diff --git a/core/java/android/app/IUidObserver.aidl b/core/java/android/app/IUidObserver.aidl
index 7713e25..74018a8 100644
--- a/core/java/android/app/IUidObserver.aidl
+++ b/core/java/android/app/IUidObserver.aidl
@@ -24,7 +24,7 @@
// below block of transactions.
// Since these transactions are also called from native code, these must be kept in sync with
- // the ones in frameworks/native/include/binder/IActivityManager.h
+ // the ones in frameworks/native/include_activitymanager/binder/IActivityManager.h
// =============== Beginning of transactions used on native side as well ======================
/**
diff --git a/core/java/android/net/EthernetNetworkSpecifier.java b/core/java/android/net/EthernetNetworkSpecifier.java
index e168588..62c5761 100644
--- a/core/java/android/net/EthernetNetworkSpecifier.java
+++ b/core/java/android/net/EthernetNetworkSpecifier.java
@@ -42,15 +42,22 @@
@NonNull
private final String mInterfaceName;
+ /**
+ * Create a new EthernetNetworkSpecifier.
+ * @param interfaceName Name of the ethernet interface the specifier refers to.
+ */
public EthernetNetworkSpecifier(@NonNull String interfaceName) {
Preconditions.checkStringNotEmpty(interfaceName);
mInterfaceName = interfaceName;
}
- // This may be null in the future to support specifiers based on data other than the interface
- // name.
+ /**
+ * Get the name of the ethernet interface the specifier refers to.
+ */
@Nullable
public String getInterfaceName() {
+ // This may be null in the future to support specifiers based on data other than the
+ // interface name.
return mInterfaceName;
}
diff --git a/core/java/android/os/BatteryStatsManager.java b/core/java/android/os/BatteryStatsManager.java
index 3f4a218..f5172cf 100644
--- a/core/java/android/os/BatteryStatsManager.java
+++ b/core/java/android/os/BatteryStatsManager.java
@@ -386,10 +386,9 @@
* @param uid Uid of this event. For the active state it represents the uid that was responsible
* for waking the radio, or -1 if the system was responsible for waking the radio.
* For inactive state, the UID should always be -1.
- * @throws RuntimeException if there are binder remote-invocation errors.
*/
@RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS)
- public void reportMobileRadioPowerState(boolean isActive, int uid) throws RuntimeException {
+ public void reportMobileRadioPowerState(boolean isActive, int uid) {
try {
mBatteryStats.noteMobileRadioPowerState(getDataConnectionPowerState(isActive),
SystemClock.elapsedRealtimeNanos(), uid);
@@ -405,10 +404,9 @@
* @param uid Uid of this event. For the active state it represents the uid that was responsible
* for waking the radio, or -1 if the system was responsible for waking the radio.
* For inactive state, the UID should always be -1.
- * @throws RuntimeException if there are binder remote-invocation errors.
*/
@RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS)
- public void reportWifiRadioPowerState(boolean isActive, int uid) throws RuntimeException {
+ public void reportWifiRadioPowerState(boolean isActive, int uid) {
try {
mBatteryStats.noteWifiRadioPowerState(getDataConnectionPowerState(isActive),
SystemClock.elapsedRealtimeNanos(), uid);
diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java
index 03caafd..e47ffcc 100755
--- a/core/java/android/os/Build.java
+++ b/core/java/android/os/Build.java
@@ -1309,10 +1309,25 @@
* Debuggable builds allow users to gain root access via local shell, attach debuggers to any
* application regardless of whether they have the "debuggable" attribute set, or downgrade
* selinux into "permissive" mode in particular.
+ * @hide
*/
public static final boolean IS_DEBUGGABLE =
SystemProperties.getInt("ro.debuggable", 0) == 1;
+ /**
+ * Returns true if the device is running a debuggable build such as "userdebug" or "eng".
+ *
+ * Debuggable builds allow users to gain root access via local shell, attach debuggers to any
+ * application regardless of whether they have the "debuggable" attribute set, or downgrade
+ * selinux into "permissive" mode in particular.
+ * @hide
+ */
+ @TestApi
+ @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+ public static boolean isDebuggable() {
+ return IS_DEBUGGABLE;
+ }
+
/** {@hide} */
public static final boolean IS_ENG = "eng".equals(TYPE);
/** {@hide} */
diff --git a/core/java/android/os/IRecoverySystem.aidl b/core/java/android/os/IRecoverySystem.aidl
index 2052883..9368b68 100644
--- a/core/java/android/os/IRecoverySystem.aidl
+++ b/core/java/android/os/IRecoverySystem.aidl
@@ -30,6 +30,6 @@
boolean requestLskf(in String packageName, in IntentSender sender);
boolean clearLskf(in String packageName);
boolean isLskfCaptured(in String packageName);
- boolean rebootWithLskfAssumeSlotSwitch(in String packageName, in String reason);
- boolean rebootWithLskf(in String packageName, in String reason, in boolean slotSwitch);
+ int rebootWithLskfAssumeSlotSwitch(in String packageName, in String reason);
+ int rebootWithLskf(in String packageName, in String reason, in boolean slotSwitch);
}
diff --git a/core/java/android/os/RecoverySystem.java b/core/java/android/os/RecoverySystem.java
index 43184ea..a42448c 100644
--- a/core/java/android/os/RecoverySystem.java
+++ b/core/java/android/os/RecoverySystem.java
@@ -20,6 +20,7 @@
import static java.nio.charset.StandardCharsets.UTF_8;
+import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
@@ -155,6 +156,65 @@
private final IRecoverySystem mService;
/**
+ * The error codes for reboots initiated by resume on reboot clients.
+ * @hide
+ */
+ @IntDef(prefix = { "RESUME_ON_REBOOT_REBOOT_ERROR_" }, value = {
+ RESUME_ON_REBOOT_REBOOT_ERROR_NONE,
+ RESUME_ON_REBOOT_REBOOT_ERROR_UNSPECIFIED,
+ RESUME_ON_REBOOT_REBOOT_ERROR_INVALID_PACKAGE_NAME,
+ RESUME_ON_REBOOT_REBOOT_ERROR_LSKF_NOT_CAPTURED,
+ RESUME_ON_REBOOT_REBOOT_ERROR_SLOT_MISMATCH,
+ RESUME_ON_REBOOT_REBOOT_ERROR_PROVIDER_PREPARATION_FAILURE})
+ public @interface ResumeOnRebootRebootErrorCode {}
+
+ /**
+ * The preparation of resume on reboot succeeds. Don't expose it because a successful reboot
+ * should just reboot the device.
+ * @hide
+ */
+ public static final int RESUME_ON_REBOOT_REBOOT_ERROR_NONE = 0;
+
+ /**
+ * The resume on reboot fails due to an unknown reason.
+ * @hide
+ */
+ @SystemApi
+ public static final int RESUME_ON_REBOOT_REBOOT_ERROR_UNSPECIFIED = 1000;
+
+ /**
+ * The resume on reboot fails because the package name of the client is invalid, e.g. null
+ * packageName, name contains invalid characters, etc.
+ * @hide
+ */
+ @SystemApi
+ public static final int RESUME_ON_REBOOT_REBOOT_ERROR_INVALID_PACKAGE_NAME = 2000;
+
+ /**
+ * The resume on reboot fails because the Lock Screen Knowledge Factor hasn't been captured.
+ * This error is also reported if the client attempts to reboot without preparing RoR.
+ * @hide
+ */
+ @SystemApi
+ public static final int RESUME_ON_REBOOT_REBOOT_ERROR_LSKF_NOT_CAPTURED = 3000;
+
+ /**
+ * The resume on reboot fails because the client expects a different boot slot for the next boot
+ * on A/B devices.
+ * @hide
+ */
+ @SystemApi
+ public static final int RESUME_ON_REBOOT_REBOOT_ERROR_SLOT_MISMATCH = 4000;
+
+ /**
+ * The resume on reboot fails because the resume on reboot provider, e.g. HAL / server based,
+ * fails to arm/store the escrow key.
+ * @hide
+ */
+ @SystemApi
+ public static final int RESUME_ON_REBOOT_REBOOT_ERROR_PROVIDER_PREPARATION_FAILURE = 5000;
+
+ /**
* Interface definition for a callback to be invoked regularly as
* verification proceeds.
*/
@@ -723,7 +783,8 @@
}
RecoverySystem rs = (RecoverySystem) context.getSystemService(Context.RECOVERY_SERVICE);
// OTA is the sole user, who expects a slot switch.
- if (!rs.rebootWithLskfAssumeSlotSwitch(context.getPackageName(), reason)) {
+ if (rs.rebootWithLskfAssumeSlotSwitch(context.getPackageName(), reason)
+ != RESUME_ON_REBOOT_REBOOT_ERROR_NONE) {
throw new IOException("system not prepared to apply update");
}
}
@@ -752,19 +813,19 @@
* @param context the Context to use.
* @param reason the reboot reason to give to the {@link PowerManager}
* @param slotSwitch true if the caller expects the slot to be switched on A/B devices.
- * @throws IOException if the reboot couldn't proceed because the device wasn't ready for an
- * unattended reboot.
+ *
+ * @return 0 on success, and a non-zero error code if the reboot couldn't proceed because the
+ * device wasn't ready for an unattended reboot.
+ * @throws IOException on remote exceptions from the RecoverySystemService
* @hide
*/
@SystemApi
@RequiresPermission(anyOf = {android.Manifest.permission.RECOVERY,
android.Manifest.permission.REBOOT})
- public static void rebootAndApply(@NonNull Context context,
+ public static @ResumeOnRebootRebootErrorCode int rebootAndApply(@NonNull Context context,
@NonNull String reason, boolean slotSwitch) throws IOException {
RecoverySystem rs = context.getSystemService(RecoverySystem.class);
- if (!rs.rebootWithLskf(context.getPackageName(), reason, slotSwitch)) {
- throw new IOException("system not prepared to apply update");
- }
+ return rs.rebootWithLskf(context.getPackageName(), reason, slotSwitch);
}
/**
@@ -1399,8 +1460,8 @@
* Calls the recovery system service to reboot and apply update.
*
*/
- private boolean rebootWithLskf(String packageName, String reason, boolean slotSwitch)
- throws IOException {
+ private @ResumeOnRebootRebootErrorCode int rebootWithLskf(String packageName, String reason,
+ boolean slotSwitch) throws IOException {
try {
return mService.rebootWithLskf(packageName, reason, slotSwitch);
} catch (RemoteException e) {
@@ -1414,8 +1475,8 @@
* expects a slot switch for A/B devices.
*
*/
- private boolean rebootWithLskfAssumeSlotSwitch(String packageName, String reason)
- throws IOException {
+ private @ResumeOnRebootRebootErrorCode int rebootWithLskfAssumeSlotSwitch(String packageName,
+ String reason) throws IOException {
try {
return mService.rebootWithLskfAssumeSlotSwitch(packageName, reason);
} catch (RemoteException e) {
diff --git a/core/java/android/uwb/IUwbRangingCallbacks.aidl b/core/java/android/uwb/IUwbRangingCallbacks.aidl
index f71f3ff..f15debb 100644
--- a/core/java/android/uwb/IUwbRangingCallbacks.aidl
+++ b/core/java/android/uwb/IUwbRangingCallbacks.aidl
@@ -92,9 +92,13 @@
* Called when the ranging session has been stopped
*
* @param sessionHandle the session the callback is being invoked for
+ * @param reason the reason the session was stopped
+ * @param parameters protocol specific parameters
*/
- void onRangingStopped(in SessionHandle sessionHandle);
+ void onRangingStopped(in SessionHandle sessionHandle,
+ RangingChangeReason reason,
+ in PersistableBundle parameters);
/**
* Called when a ranging session fails to stop
diff --git a/core/java/android/uwb/RangingManager.java b/core/java/android/uwb/RangingManager.java
index 85f2c1c..e2c64a7 100644
--- a/core/java/android/uwb/RangingManager.java
+++ b/core/java/android/uwb/RangingManager.java
@@ -165,7 +165,8 @@
}
@Override
- public void onRangingStopped(SessionHandle sessionHandle) {
+ public void onRangingStopped(SessionHandle sessionHandle, @RangingChangeReason int reason,
+ PersistableBundle params) {
synchronized (this) {
if (!hasSession(sessionHandle)) {
Log.w(TAG, "onRangingStopped - received unexpected SessionHandle: "
@@ -174,7 +175,7 @@
}
RangingSession session = mRangingSessionTable.get(sessionHandle);
- session.onRangingStopped();
+ session.onRangingStopped(convertToReason(reason), params);
}
}
diff --git a/core/java/android/uwb/RangingSession.java b/core/java/android/uwb/RangingSession.java
index 52ec5bd..345b69d 100644
--- a/core/java/android/uwb/RangingSession.java
+++ b/core/java/android/uwb/RangingSession.java
@@ -191,8 +191,11 @@
/**
* Invoked when a request to stop the session succeeds
+ *
+ * @param reason reason for the session stop
+ * @param parameters protocol specific parameters related to the stop reason
*/
- void onStopped();
+ void onStopped(@Reason int reason, @NonNull PersistableBundle parameters);
/**
* Invoked when a request to stop the session fails
@@ -434,14 +437,15 @@
/**
* @hide
*/
- public void onRangingStopped() {
+ public void onRangingStopped(@Callback.Reason int reason,
+ @NonNull PersistableBundle params) {
if (mState == State.CLOSED) {
Log.w(TAG, "onRangingStopped invoked for a closed session");
return;
}
mState = State.IDLE;
- executeCallback(() -> mCallback.onStopped());
+ executeCallback(() -> mCallback.onStopped(reason, params));
}
/**
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index ed84434..855448b 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -50,6 +50,7 @@
#include "jni.h"
using namespace android;
+using android::base::GetBoolProperty;
using android::base::GetProperty;
extern int register_android_os_Binder(JNIEnv* env);
@@ -727,17 +728,7 @@
ALOGI("Leaving lock profiling enabled");
}
- bool checkJni = false;
- property_get("dalvik.vm.checkjni", propBuf, "");
- if (strcmp(propBuf, "true") == 0) {
- checkJni = true;
- } else if (strcmp(propBuf, "false") != 0) {
- /* property is neither true nor false; fall back on kernel parameter */
- property_get("ro.kernel.android.checkjni", propBuf, "");
- if (propBuf[0] == '1') {
- checkJni = true;
- }
- }
+ const bool checkJni = GetBoolProperty("dalvik.vm.checkjni", false);
ALOGV("CheckJNI is %s\n", checkJni ? "ON" : "OFF");
if (checkJni) {
/* extended JNI checking */
diff --git a/core/tests/uwbtests/src/android/uwb/RangingManagerTest.java b/core/tests/uwbtests/src/android/uwb/RangingManagerTest.java
index e41805d..21ef083 100644
--- a/core/tests/uwbtests/src/android/uwb/RangingManagerTest.java
+++ b/core/tests/uwbtests/src/android/uwb/RangingManagerTest.java
@@ -123,8 +123,8 @@
rangingManager.onRangingReconfigureFailed(handle, REASON, PARAMS);
verify(callback, times(1)).onReconfigureFailed(eq(REASON), eq(PARAMS));
- rangingManager.onRangingStopped(handle);
- verify(callback, times(1)).onStopped();
+ rangingManager.onRangingStopped(handle, REASON, PARAMS);
+ verify(callback, times(1)).onStopped(eq(REASON), eq(PARAMS));
rangingManager.onRangingStopFailed(handle, REASON, PARAMS);
verify(callback, times(1)).onStopFailed(eq(REASON), eq(PARAMS));
diff --git a/keystore/java/android/security/keystore/KeyGenParameterSpec.java b/keystore/java/android/security/keystore/KeyGenParameterSpec.java
index 1f9022b..a6aa4f2 100644
--- a/keystore/java/android/security/keystore/KeyGenParameterSpec.java
+++ b/keystore/java/android/security/keystore/KeyGenParameterSpec.java
@@ -353,7 +353,7 @@
boolean userPresenceRequired,
byte[] attestationChallenge,
boolean devicePropertiesAttestationIncluded,
- int[] attestationIds,
+ @NonNull int[] attestationIds,
boolean uniqueIdIncluded,
boolean userAuthenticationValidWhileOnBody,
boolean invalidatedByBiometricEnrollment,
@@ -779,9 +779,8 @@
* @return integer array representing the requested device IDs to attest.
*/
@SystemApi
- @Nullable
- public int[] getAttestationIds() {
- return Utils.cloneIfNotNull(mAttestationIds);
+ public @NonNull int[] getAttestationIds() {
+ return mAttestationIds.clone();
}
/**
@@ -911,7 +910,7 @@
private boolean mUserPresenceRequired = false;
private byte[] mAttestationChallenge = null;
private boolean mDevicePropertiesAttestationIncluded = false;
- private int[] mAttestationIds = null;
+ private int[] mAttestationIds = new int[0];
private boolean mUniqueIdIncluded = false;
private boolean mUserAuthenticationValidWhileOnBody;
private boolean mInvalidatedByBiometricEnrollment = true;
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java
index c26d9f583..dc7f3dd 100644
--- a/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java
+++ b/keystore/java/android/security/keystore2/AndroidKeyStoreKeyPairGeneratorSpi.java
@@ -655,7 +655,7 @@
}
int[] idTypes = mSpec.getAttestationIds();
- if (idTypes == null) {
+ if (idTypes.length == 0) {
return;
}
final Set<Integer> idTypesSet = new ArraySet<>(idTypes.length);
diff --git a/native/android/Android.bp b/native/android/Android.bp
index d1dddbd..34b9fce 100644
--- a/native/android/Android.bp
+++ b/native/android/Android.bp
@@ -77,6 +77,7 @@
"libgui",
"libharfbuzz_ng", // Only for including hb.h via minikin
"libsensor",
+ "libactivitymanager_aidl",
"libandroid_runtime",
"libminikin",
"libnetd_client",
diff --git a/packages/Connectivity/framework/api/current.txt b/packages/Connectivity/framework/api/current.txt
index ab290f9..7692e30 100644
--- a/packages/Connectivity/framework/api/current.txt
+++ b/packages/Connectivity/framework/api/current.txt
@@ -410,6 +410,8 @@
}
public class ParseException extends java.lang.RuntimeException {
+ ctor public ParseException(@NonNull String);
+ ctor public ParseException(@NonNull String, @NonNull Throwable);
field public String response;
}
diff --git a/packages/Connectivity/framework/api/module-lib-current.txt b/packages/Connectivity/framework/api/module-lib-current.txt
index 8dfdd614..caf7f49 100644
--- a/packages/Connectivity/framework/api/module-lib-current.txt
+++ b/packages/Connectivity/framework/api/module-lib-current.txt
@@ -133,11 +133,6 @@
method @NonNull public android.net.NetworkRequest.Builder setUids(@Nullable java.util.Set<android.util.Range<java.lang.Integer>>);
}
- public class ParseException extends java.lang.RuntimeException {
- ctor public ParseException(@NonNull String);
- ctor public ParseException(@NonNull String, @NonNull Throwable);
- }
-
public final class TcpRepairWindow {
ctor public TcpRepairWindow(int, int, int, int, int, int);
field public final int maxWindow;
@@ -158,10 +153,10 @@
}
public class TestNetworkManager {
- method @NonNull public android.net.TestNetworkInterface createTapInterface();
- method @NonNull public android.net.TestNetworkInterface createTunInterface(@NonNull java.util.Collection<android.net.LinkAddress>);
- method public void setupTestNetwork(@NonNull String, @NonNull android.os.IBinder);
- method public void teardownTestNetwork(@NonNull android.net.Network);
+ method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_TEST_NETWORKS) public android.net.TestNetworkInterface createTapInterface();
+ method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_TEST_NETWORKS) public android.net.TestNetworkInterface createTunInterface(@NonNull java.util.Collection<android.net.LinkAddress>);
+ method @RequiresPermission(android.Manifest.permission.MANAGE_TEST_NETWORKS) public void setupTestNetwork(@NonNull String, @NonNull android.os.IBinder);
+ method @RequiresPermission(android.Manifest.permission.MANAGE_TEST_NETWORKS) public void teardownTestNetwork(@NonNull android.net.Network);
field public static final String TEST_TAP_PREFIX = "testtap";
}
diff --git a/packages/Connectivity/framework/api/system-current.txt b/packages/Connectivity/framework/api/system-current.txt
index 0a82cb7..d6d3888 100644
--- a/packages/Connectivity/framework/api/system-current.txt
+++ b/packages/Connectivity/framework/api/system-current.txt
@@ -455,14 +455,14 @@
public final class TcpKeepalivePacketData extends android.net.KeepalivePacketData implements android.os.Parcelable {
ctor public TcpKeepalivePacketData(@NonNull java.net.InetAddress, int, @NonNull java.net.InetAddress, int, @NonNull byte[], int, int, int, int, int, int) throws android.net.InvalidPacketException;
method public int describeContents();
+ method public int getIpTos();
+ method public int getIpTtl();
+ method public int getTcpAck();
+ method public int getTcpSeq();
+ method public int getTcpWindow();
+ method public int getTcpWindowScale();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.net.TcpKeepalivePacketData> CREATOR;
- field public final int ipTos;
- field public final int ipTtl;
- field public final int tcpAck;
- field public final int tcpSeq;
- field public final int tcpWindow;
- field public final int tcpWindowScale;
}
}
diff --git a/packages/Connectivity/framework/src/android/net/CaptivePortalData.java b/packages/Connectivity/framework/src/android/net/CaptivePortalData.java
index 82dbd0f..53aa1b9 100644
--- a/packages/Connectivity/framework/src/android/net/CaptivePortalData.java
+++ b/packages/Connectivity/framework/src/android/net/CaptivePortalData.java
@@ -42,7 +42,7 @@
private final long mByteLimit;
private final long mExpiryTimeMillis;
private final boolean mCaptive;
- private final CharSequence mVenueFriendlyName;
+ private final String mVenueFriendlyName;
private final int mVenueInfoUrlSource;
private final int mUserPortalUrlSource;
@@ -73,14 +73,14 @@
mByteLimit = byteLimit;
mExpiryTimeMillis = expiryTimeMillis;
mCaptive = captive;
- mVenueFriendlyName = venueFriendlyName;
+ mVenueFriendlyName = venueFriendlyName == null ? null : venueFriendlyName.toString();
mVenueInfoUrlSource = venueInfoUrlSource;
mUserPortalUrlSource = userPortalUrlSource;
}
private CaptivePortalData(Parcel p) {
this(p.readLong(), p.readParcelable(null), p.readParcelable(null), p.readBoolean(),
- p.readLong(), p.readLong(), p.readBoolean(), p.readCharSequence(), p.readInt(),
+ p.readLong(), p.readLong(), p.readBoolean(), p.readString(), p.readInt(),
p.readInt());
}
@@ -98,7 +98,7 @@
dest.writeLong(mByteLimit);
dest.writeLong(mExpiryTimeMillis);
dest.writeBoolean(mCaptive);
- dest.writeCharSequence(mVenueFriendlyName);
+ dest.writeString(mVenueFriendlyName);
dest.writeInt(mVenueInfoUrlSource);
dest.writeInt(mUserPortalUrlSource);
}
diff --git a/packages/Connectivity/framework/src/android/net/ConnectivityManager.java b/packages/Connectivity/framework/src/android/net/ConnectivityManager.java
index 9fbf049..0418450 100644
--- a/packages/Connectivity/framework/src/android/net/ConnectivityManager.java
+++ b/packages/Connectivity/framework/src/android/net/ConnectivityManager.java
@@ -44,6 +44,7 @@
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
+import android.net.ConnectivityDiagnosticsManager.DataStallReport.DetectionMethod;
import android.net.IpSecManager.UdpEncapsulationSocket;
import android.net.SocketKeepalive.Callback;
import android.net.TetheringManager.StartTetheringCallback;
@@ -5104,10 +5105,13 @@
*
* <p>This method should only be used for tests.
*
- * <p>The caller must be the owner of the specified Network.
+ * <p>The caller must be the owner of the specified Network. This simulates a data stall to
+ * have the system behave as if it had happened, but does not actually stall connectivity.
*
* @param detectionMethod The detection method used to identify the Data Stall.
- * @param timestampMillis The timestamp at which the stall 'occurred', in milliseconds.
+ * See ConnectivityDiagnosticsManager.DataStallReport.DETECTION_METHOD_*.
+ * @param timestampMillis The timestamp at which the stall 'occurred', in milliseconds, as per
+ * SystemClock.elapsedRealtime.
* @param network The Network for which a Data Stall is being simluated.
* @param extras The PersistableBundle of extras included in the Data Stall notification.
* @throws SecurityException if the caller is not the owner of the given network.
@@ -5116,7 +5120,7 @@
@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
@RequiresPermission(anyOf = {android.Manifest.permission.MANAGE_TEST_NETWORKS,
android.Manifest.permission.NETWORK_STACK})
- public void simulateDataStall(int detectionMethod, long timestampMillis,
+ public void simulateDataStall(@DetectionMethod int detectionMethod, long timestampMillis,
@NonNull Network network, @NonNull PersistableBundle extras) {
try {
mService.simulateDataStall(detectionMethod, timestampMillis, network, extras);
diff --git a/packages/Connectivity/framework/src/android/net/OemNetworkPreferences.java b/packages/Connectivity/framework/src/android/net/OemNetworkPreferences.java
index 5a76cd6..2bb006d 100644
--- a/packages/Connectivity/framework/src/android/net/OemNetworkPreferences.java
+++ b/packages/Connectivity/framework/src/android/net/OemNetworkPreferences.java
@@ -40,6 +40,23 @@
*/
@SystemApi
public final class OemNetworkPreferences implements Parcelable {
+ // Valid production preferences must be > 0, negative values reserved for testing
+ /**
+ * This preference is only to be used for testing and nothing else.
+ * Use only TRANSPORT_TEST transport networks.
+ * @hide
+ */
+ public static final int OEM_NETWORK_PREFERENCE_TEST_ONLY = -2;
+
+ /**
+ * This preference is only to be used for testing and nothing else.
+ * If an unmetered network is available, use it.
+ * Otherwise, if a network with the TRANSPORT_TEST transport is available, use it.
+ * Otherwise, use the general default network.
+ * @hide
+ */
+ public static final int OEM_NETWORK_PREFERENCE_TEST = -1;
+
/**
* Default in case this value is not set. Using it will result in an error.
*/
@@ -69,6 +86,12 @@
*/
public static final int OEM_NETWORK_PREFERENCE_OEM_PRIVATE_ONLY = 4;
+ /**
+ * The max allowed value for an OEM network preference.
+ * @hide
+ */
+ public static final int OEM_NETWORK_PREFERENCE_MAX = OEM_NETWORK_PREFERENCE_OEM_PRIVATE_ONLY;
+
@NonNull
private final Bundle mNetworkMappings;
@@ -96,7 +119,7 @@
@Override
public String toString() {
- return "OemNetworkPreferences{" + "mNetworkMappings=" + mNetworkMappings + '}';
+ return "OemNetworkPreferences{" + "mNetworkMappings=" + getNetworkPreferences() + '}';
}
@Override
@@ -185,6 +208,8 @@
/** @hide */
@IntDef(prefix = "OEM_NETWORK_PREFERENCE_", value = {
+ OEM_NETWORK_PREFERENCE_TEST_ONLY,
+ OEM_NETWORK_PREFERENCE_TEST,
OEM_NETWORK_PREFERENCE_UNINITIALIZED,
OEM_NETWORK_PREFERENCE_OEM_PAID,
OEM_NETWORK_PREFERENCE_OEM_PAID_NO_FALLBACK,
@@ -205,6 +230,10 @@
@NonNull
public static String oemNetworkPreferenceToString(@OemNetworkPreference int value) {
switch (value) {
+ case OEM_NETWORK_PREFERENCE_TEST_ONLY:
+ return "OEM_NETWORK_PREFERENCE_TEST_ONLY";
+ case OEM_NETWORK_PREFERENCE_TEST:
+ return "OEM_NETWORK_PREFERENCE_TEST";
case OEM_NETWORK_PREFERENCE_UNINITIALIZED:
return "OEM_NETWORK_PREFERENCE_UNINITIALIZED";
case OEM_NETWORK_PREFERENCE_OEM_PAID:
diff --git a/packages/Connectivity/framework/src/android/net/ParseException.java b/packages/Connectivity/framework/src/android/net/ParseException.java
index ca6d012..9d4727a 100644
--- a/packages/Connectivity/framework/src/android/net/ParseException.java
+++ b/packages/Connectivity/framework/src/android/net/ParseException.java
@@ -17,7 +17,6 @@
package android.net;
import android.annotation.NonNull;
-import android.annotation.SystemApi;
/**
* Thrown when parsing failed.
@@ -26,15 +25,11 @@
public class ParseException extends RuntimeException {
public String response;
- /** @hide */
- @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
public ParseException(@NonNull String response) {
super(response);
this.response = response;
}
- /** @hide */
- @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
public ParseException(@NonNull String response, @NonNull Throwable cause) {
super(response, cause);
this.response = response;
diff --git a/packages/Connectivity/framework/src/android/net/TcpKeepalivePacketData.java b/packages/Connectivity/framework/src/android/net/TcpKeepalivePacketData.java
index ddb3a6a7..c2c4f32 100644
--- a/packages/Connectivity/framework/src/android/net/TcpKeepalivePacketData.java
+++ b/packages/Connectivity/framework/src/android/net/TcpKeepalivePacketData.java
@@ -32,22 +32,39 @@
public final class TcpKeepalivePacketData extends KeepalivePacketData implements Parcelable {
private static final String TAG = "TcpKeepalivePacketData";
- /** TCP sequence number. */
+ /**
+ * TCP sequence number.
+ * @hide
+ */
public final int tcpSeq;
- /** TCP ACK number. */
+ /**
+ * TCP ACK number.
+ * @hide
+ */
public final int tcpAck;
- /** TCP RCV window. */
+ /**
+ * TCP RCV window.
+ * @hide
+ */
public final int tcpWindow;
- /** TCP RCV window scale. */
+ /** TCP RCV window scale.
+ * @hide
+ */
public final int tcpWindowScale;
- /** IP TOS. */
+ /**
+ * IP TOS.
+ * @hide
+ */
public final int ipTos;
- /** IP TTL. */
+ /**
+ * IP TTL.
+ * @hide
+ */
public final int ipTtl;
public TcpKeepalivePacketData(@NonNull final InetAddress srcAddress, int srcPort,
@@ -63,6 +80,56 @@
this.ipTtl = ipTtl;
}
+ /**
+ * Get the TCP sequence number.
+ *
+ * See https://tools.ietf.org/html/rfc793#page-15.
+ */
+ public int getTcpSeq() {
+ return tcpSeq;
+ }
+
+ /**
+ * Get the TCP ACK number.
+ *
+ * See https://tools.ietf.org/html/rfc793#page-15.
+ */
+ public int getTcpAck() {
+ return tcpAck;
+ }
+
+ /**
+ * Get the TCP RCV window.
+ *
+ * See https://tools.ietf.org/html/rfc793#page-15.
+ */
+ public int getTcpWindow() {
+ return tcpWindow;
+ }
+
+ /**
+ * Get the TCP RCV window scale.
+ *
+ * See https://tools.ietf.org/html/rfc793#page-15.
+ */
+ public int getTcpWindowScale() {
+ return tcpWindowScale;
+ }
+
+ /**
+ * Get the IP type of service.
+ */
+ public int getIpTos() {
+ return ipTos;
+ }
+
+ /**
+ * Get the IP TTL.
+ */
+ public int getIpTtl() {
+ return ipTtl;
+ }
+
@Override
public boolean equals(@Nullable final Object o) {
if (!(o instanceof TcpKeepalivePacketData)) return false;
diff --git a/packages/Connectivity/framework/src/android/net/TestNetworkManager.java b/packages/Connectivity/framework/src/android/net/TestNetworkManager.java
index a7a6235..9ddd2f5 100644
--- a/packages/Connectivity/framework/src/android/net/TestNetworkManager.java
+++ b/packages/Connectivity/framework/src/android/net/TestNetworkManager.java
@@ -15,8 +15,10 @@
*/
package android.net;
+import android.Manifest;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.os.IBinder;
import android.os.RemoteException;
@@ -58,6 +60,7 @@
* @param network The test network that should be torn down
* @hide
*/
+ @RequiresPermission(Manifest.permission.MANAGE_TEST_NETWORKS)
@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
public void teardownTestNetwork(@NonNull Network network) {
try {
@@ -103,6 +106,7 @@
* @param binder A binder object guarding the lifecycle of this test network.
* @hide
*/
+ @RequiresPermission(Manifest.permission.MANAGE_TEST_NETWORKS)
@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
public void setupTestNetwork(@NonNull String iface, @NonNull IBinder binder) {
setupTestNetwork(iface, null, true, new int[0], binder);
@@ -145,6 +149,7 @@
* TUN interface.
* @hide
*/
+ @RequiresPermission(Manifest.permission.MANAGE_TEST_NETWORKS)
@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
@NonNull
public TestNetworkInterface createTunInterface(@NonNull Collection<LinkAddress> linkAddrs) {
@@ -163,6 +168,7 @@
* TAP interface.
* @hide
*/
+ @RequiresPermission(Manifest.permission.MANAGE_TEST_NETWORKS)
@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
@NonNull
public TestNetworkInterface createTapInterface() {
diff --git a/services/backup/OWNERS b/services/backup/OWNERS
index cc36b47..852c689 100644
--- a/services/backup/OWNERS
+++ b/services/backup/OWNERS
@@ -1,6 +1,5 @@
# Bug component: 656484
-aabhinav@google.com
bryanmawhinney@google.com
jstemmer@google.com
millmore@google.com
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 5aa3e5b..2e974ae 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -2884,13 +2884,13 @@
pw.println();
pw.println("mNetworkRequestInfoLogs (most recent first):");
pw.increaseIndent();
- mNetworkRequestInfoLogs.reverseDump(fd, pw, args);
+ mNetworkRequestInfoLogs.reverseDump(pw);
pw.decreaseIndent();
pw.println();
pw.println("mNetworkInfoBlockingLogs (most recent first):");
pw.increaseIndent();
- mNetworkInfoBlockingLogs.reverseDump(fd, pw, args);
+ mNetworkInfoBlockingLogs.reverseDump(pw);
pw.decreaseIndent();
pw.println();
@@ -2904,7 +2904,7 @@
long duration = SystemClock.elapsedRealtime() - mLastWakeLockAcquireTimestamp;
pw.println("currently holding WakeLock for: " + (duration / 1000) + "s");
}
- mWakelockLogs.reverseDump(fd, pw, args);
+ mWakelockLogs.reverseDump(pw);
pw.println();
pw.println("bandwidth update requests (by uid):");
@@ -2916,7 +2916,12 @@
}
}
pw.decreaseIndent();
+ pw.decreaseIndent();
+ pw.println();
+ pw.println("mOemNetworkPreferencesLogs (most recent first):");
+ pw.increaseIndent();
+ mOemNetworkPreferencesLogs.reverseDump(pw);
pw.decreaseIndent();
}
@@ -6205,6 +6210,12 @@
@NonNull
private ProfileNetworkPreferences mProfileNetworkPreferences = new ProfileNetworkPreferences();
+ // OemNetworkPreferences activity String log entries.
+ private static final int MAX_OEM_NETWORK_PREFERENCE_LOGS = 20;
+ @NonNull
+ private final LocalLog mOemNetworkPreferencesLogs =
+ new LocalLog(MAX_OEM_NETWORK_PREFERENCE_LOGS);
+
/**
* Determine whether a given package has a mapping in the current OemNetworkPreferences.
* @param packageName the package name to check existence of a mapping for.
@@ -7677,7 +7688,7 @@
}
void addRequestReassignment(@NonNull final RequestReassignment reassignment) {
- if (Build.IS_DEBUGGABLE) {
+ if (Build.isDebuggable()) {
// The code is never supposed to add two reassignments of the same request. Make
// sure this stays true, but without imposing this expensive check on all
// reassignments on all user devices.
@@ -9637,6 +9648,7 @@
return;
}
+ mOemNetworkPreferencesLogs.log("UPDATE INITIATED: " + preference);
final ArraySet<NetworkRequestInfo> nris =
new OemNetworkRequestFactory().createNrisFromOemNetworkPreferences(preference);
replaceDefaultNetworkRequestsForPreference(nris);
diff --git a/services/core/java/com/android/server/VcnManagementService.java b/services/core/java/com/android/server/VcnManagementService.java
index f7ae58c..9c10658 100644
--- a/services/core/java/com/android/server/VcnManagementService.java
+++ b/services/core/java/com/android/server/VcnManagementService.java
@@ -16,6 +16,7 @@
package com.android.server;
+import static android.Manifest.permission.DUMP;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED;
import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
import static android.net.vcn.VcnManager.VCN_STATUS_CODE_ACTIVE;
@@ -69,6 +70,7 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.annotations.VisibleForTesting.Visibility;
+import com.android.internal.util.IndentingPrintWriter;
import com.android.net.module.util.LocationPermissionChecker;
import com.android.server.vcn.TelephonySubscriptionTracker;
import com.android.server.vcn.Vcn;
@@ -76,7 +78,9 @@
import com.android.server.vcn.VcnNetworkProvider;
import com.android.server.vcn.util.PersistableBundleUtils;
+import java.io.FileDescriptor;
import java.io.IOException;
+import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@@ -168,9 +172,6 @@
@NonNull
private final TrackingNetworkCallback mTrackingNetworkCallback = new TrackingNetworkCallback();
- /** Can only be assigned when {@link #systemReady()} is called, since it uses AppOpsManager. */
- @Nullable private LocationPermissionChecker mLocationPermissionChecker;
-
@GuardedBy("mLock")
@NonNull
private final Map<ParcelUuid, VcnConfig> mConfigs = new ArrayMap<>();
@@ -369,7 +370,6 @@
new NetworkRequest.Builder().clearCapabilities().build(),
mTrackingNetworkCallback);
mTelephonySubscriptionTracker.register();
- mLocationPermissionChecker = mDeps.newLocationPermissionChecker(mVcnContext.getContext());
}
private void enforcePrimaryUser() {
@@ -836,13 +836,6 @@
return false;
}
- if (!mLocationPermissionChecker.checkLocationPermission(
- cbInfo.mPkgName,
- "VcnStatusCallback" /* featureId */,
- cbInfo.mUid,
- null /* message */)) {
- return false;
- }
return true;
}
@@ -929,6 +922,33 @@
}
}
+ /**
+ * Dumps the state of the VcnManagementService for logging and debugging purposes.
+ *
+ * <p>PII and credentials MUST NEVER be dumped here.
+ */
+ @Override
+ protected void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
+ mContext.enforceCallingOrSelfPermission(DUMP, TAG);
+
+ final IndentingPrintWriter pw = new IndentingPrintWriter(writer, " ");
+
+ pw.println("VcnManagementService dump:");
+ pw.increaseIndent();
+
+ mNetworkProvider.dump(pw);
+
+ synchronized (mLock) {
+ pw.println("mVcns:");
+ for (Vcn vcn : mVcns.values()) {
+ vcn.dump(pw);
+ }
+ pw.println();
+ }
+
+ pw.decreaseIndent();
+ }
+
// TODO(b/180452282): Make name more generic and implement directly with VcnManagementService
/** Callback for Vcn signals sent up to VcnManagementService. */
public interface VcnCallback {
diff --git a/services/core/java/com/android/server/clipboard/ClipboardService.java b/services/core/java/com/android/server/clipboard/ClipboardService.java
index 72160c2..f4e06d3 100644
--- a/services/core/java/com/android/server/clipboard/ClipboardService.java
+++ b/services/core/java/com/android/server/clipboard/ClipboardService.java
@@ -52,10 +52,6 @@
import android.os.UserHandle;
import android.os.UserManager;
import android.provider.Settings;
-import android.system.ErrnoException;
-import android.system.Os;
-import android.system.OsConstants;
-import android.system.VmSocketAddress;
import android.text.TextUtils;
import android.util.Slog;
import android.util.SparseArray;
@@ -67,128 +63,9 @@
import com.android.server.uri.UriGrantsManagerInternal;
import com.android.server.wm.WindowManagerInternal;
-import java.io.FileDescriptor;
-import java.io.InterruptedIOException;
-import java.net.SocketException;
-import java.nio.ByteBuffer;
-import java.nio.ByteOrder;
-import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
-
-// The following class is Android Emulator specific. It is used to read and
-// write contents of the host system's clipboard.
-class HostClipboardMonitor implements Runnable {
- public interface HostClipboardCallback {
- void onHostClipboardUpdated(String contents);
- }
-
- private FileDescriptor mPipe = null;
- private HostClipboardCallback mHostClipboardCallback;
- private static final String PIPE_NAME = "pipe:clipboard";
- private static final int HOST_PORT = 5000;
-
- private static byte[] createOpenHandshake() {
- // String.getBytes doesn't include the null terminator,
- // but the QEMU pipe device requires the pipe service name
- // to be null-terminated.
-
- final byte[] bits = Arrays.copyOf(PIPE_NAME.getBytes(), PIPE_NAME.length() + 1);
- bits[PIPE_NAME.length()] = 0;
- return bits;
- }
-
- private boolean openPipe() {
- try {
- final FileDescriptor fd = Os.socket(OsConstants.AF_VSOCK, OsConstants.SOCK_STREAM, 0);
-
- try {
- Os.connect(fd, new VmSocketAddress(HOST_PORT, OsConstants.VMADDR_CID_HOST));
-
- final byte[] handshake = createOpenHandshake();
- Os.write(fd, handshake, 0, handshake.length);
- mPipe = fd;
- return true;
- } catch (ErrnoException | SocketException | InterruptedIOException e) {
- Os.close(fd);
- }
- } catch (ErrnoException e) {
- }
-
- return false;
- }
-
- private void closePipe() {
- try {
- final FileDescriptor fd = mPipe;
- mPipe = null;
- if (fd != null) {
- Os.close(fd);
- }
- } catch (ErrnoException ignore) {
- }
- }
-
- private byte[] receiveMessage() throws ErrnoException, InterruptedIOException {
- final byte[] lengthBits = new byte[4];
- Os.read(mPipe, lengthBits, 0, lengthBits.length);
-
- final ByteBuffer bb = ByteBuffer.wrap(lengthBits);
- bb.order(ByteOrder.LITTLE_ENDIAN);
- final int msgLen = bb.getInt();
-
- final byte[] msg = new byte[msgLen];
- Os.read(mPipe, msg, 0, msg.length);
-
- return msg;
- }
-
- private void sendMessage(byte[] msg) throws ErrnoException, InterruptedIOException {
- final byte[] lengthBits = new byte[4];
- final ByteBuffer bb = ByteBuffer.wrap(lengthBits);
- bb.order(ByteOrder.LITTLE_ENDIAN);
- bb.putInt(msg.length);
-
- Os.write(mPipe, lengthBits, 0, lengthBits.length);
- Os.write(mPipe, msg, 0, msg.length);
- }
-
- public HostClipboardMonitor(HostClipboardCallback cb) {
- mHostClipboardCallback = cb;
- }
-
- @Override
- public void run() {
- while (!Thread.interrupted()) {
- try {
- // There's no guarantee that QEMU pipes will be ready at the moment
- // this method is invoked. We simply try to get the pipe open and
- // retry on failure indefinitely.
- while ((mPipe == null) && !openPipe()) {
- Thread.sleep(100);
- }
-
- final byte[] receivedData = receiveMessage();
- mHostClipboardCallback.onHostClipboardUpdated(
- new String(receivedData));
- } catch (ErrnoException | InterruptedIOException e) {
- closePipe();
- } catch (InterruptedException e) {
- }
- }
- }
-
- public void setHostClipboard(String content) {
- try {
- if (mPipe != null) {
- sendMessage(content.getBytes());
- }
- } catch (ErrnoException | InterruptedIOException e) {
- Slog.e("HostClipboardMonitor",
- "Failed to set host clipboard " + e.getMessage());
- }
- }
-}
+import java.util.function.Consumer;
/**
* Implementation of the clipboard for copy and paste.
@@ -214,8 +91,7 @@
private final ContentCaptureManagerInternal mContentCaptureInternal;
private final AutofillManagerInternal mAutofillInternal;
private final IBinder mPermissionOwner;
- private HostClipboardMonitor mHostClipboardMonitor = null;
- private Thread mHostMonitorThread = null;
+ private final Consumer<ClipData> mEmulatorClipboardMonitor;
private final SparseArray<PerUserClipboard> mClipboards = new SparseArray<>();
@@ -237,22 +113,13 @@
final IBinder permOwner = mUgmInternal.newUriPermissionOwner("clipboard");
mPermissionOwner = permOwner;
if (IS_EMULATOR) {
- mHostClipboardMonitor = new HostClipboardMonitor(
- new HostClipboardMonitor.HostClipboardCallback() {
- @Override
- public void onHostClipboardUpdated(String contents){
- ClipData clip =
- new ClipData("host clipboard",
- new String[]{"text/plain"},
- new ClipData.Item(contents));
- synchronized(mClipboards) {
- setPrimaryClipInternal(getClipboard(0), clip,
- android.os.Process.SYSTEM_UID);
- }
- }
- });
- mHostMonitorThread = new Thread(mHostClipboardMonitor);
- mHostMonitorThread.start();
+ mEmulatorClipboardMonitor = new EmulatorClipboardMonitor((clip) -> {
+ synchronized (this) {
+ setPrimaryClipInternal(getClipboard(0), clip, android.os.Process.SYSTEM_UID);
+ }
+ });
+ } else {
+ mEmulatorClipboardMonitor = (clip) -> {};
}
}
@@ -547,18 +414,7 @@
}
void setPrimaryClipInternal(@Nullable ClipData clip, int uid) {
- // Push clipboard to host, if any
- if (mHostClipboardMonitor != null) {
- if (clip == null) {
- // Someone really wants the clipboard cleared, so push empty
- mHostClipboardMonitor.setHostClipboard("");
- } else if (clip.getItemCount() > 0) {
- final CharSequence text = clip.getItemAt(0).getText();
- if (text != null) {
- mHostClipboardMonitor.setHostClipboard(text.toString());
- }
- }
- }
+ mEmulatorClipboardMonitor.accept(clip);
// Update this user
final int userId = UserHandle.getUserId(uid);
diff --git a/services/core/java/com/android/server/clipboard/EmulatorClipboardMonitor.java b/services/core/java/com/android/server/clipboard/EmulatorClipboardMonitor.java
new file mode 100644
index 0000000..62b701a
--- /dev/null
+++ b/services/core/java/com/android/server/clipboard/EmulatorClipboardMonitor.java
@@ -0,0 +1,168 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.clipboard;
+
+import android.annotation.Nullable;
+import android.content.ClipData;
+import android.system.ErrnoException;
+import android.system.Os;
+import android.system.OsConstants;
+import android.system.VmSocketAddress;
+import android.util.Slog;
+
+import java.io.FileDescriptor;
+import java.io.InterruptedIOException;
+import java.net.SocketException;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.util.Arrays;
+import java.util.function.Consumer;
+
+// The following class is Android Emulator specific. It is used to read and
+// write contents of the host system's clipboard.
+class EmulatorClipboardMonitor implements Consumer<ClipData> {
+ private static final String TAG = "EmulatorClipboardMonitor";
+ private static final String PIPE_NAME = "pipe:clipboard";
+ private static final int HOST_PORT = 5000;
+ private final Thread mHostMonitorThread;
+ private FileDescriptor mPipe = null;
+
+ private static byte[] createOpenHandshake() {
+ // String.getBytes doesn't include the null terminator,
+ // but the QEMU pipe device requires the pipe service name
+ // to be null-terminated.
+
+ final byte[] bits = Arrays.copyOf(PIPE_NAME.getBytes(), PIPE_NAME.length() + 1);
+ bits[PIPE_NAME.length()] = 0;
+ return bits;
+ }
+
+ private boolean isPipeOpened() {
+ return mPipe != null;
+ }
+
+ private synchronized boolean openPipe() {
+ if (mPipe != null) {
+ return true;
+ }
+
+ try {
+ final FileDescriptor fd = Os.socket(OsConstants.AF_VSOCK, OsConstants.SOCK_STREAM, 0);
+
+ try {
+ Os.connect(fd, new VmSocketAddress(HOST_PORT, OsConstants.VMADDR_CID_HOST));
+
+ final byte[] handshake = createOpenHandshake();
+ Os.write(fd, handshake, 0, handshake.length);
+ mPipe = fd;
+ return true;
+ } catch (ErrnoException | SocketException | InterruptedIOException e) {
+ Os.close(fd);
+ }
+ } catch (ErrnoException e) {
+ }
+
+ return false;
+ }
+
+ private synchronized void closePipe() {
+ try {
+ final FileDescriptor fd = mPipe;
+ mPipe = null;
+ if (fd != null) {
+ Os.close(fd);
+ }
+ } catch (ErrnoException ignore) {
+ }
+ }
+
+ private byte[] receiveMessage() throws ErrnoException, InterruptedIOException {
+ final byte[] lengthBits = new byte[4];
+ Os.read(mPipe, lengthBits, 0, lengthBits.length);
+
+ final ByteBuffer bb = ByteBuffer.wrap(lengthBits);
+ bb.order(ByteOrder.LITTLE_ENDIAN);
+ final int msgLen = bb.getInt();
+
+ final byte[] msg = new byte[msgLen];
+ Os.read(mPipe, msg, 0, msg.length);
+
+ return msg;
+ }
+
+ private void sendMessage(final byte[] msg) throws ErrnoException, InterruptedIOException {
+ final byte[] lengthBits = new byte[4];
+ final ByteBuffer bb = ByteBuffer.wrap(lengthBits);
+ bb.order(ByteOrder.LITTLE_ENDIAN);
+ bb.putInt(msg.length);
+
+ Os.write(mPipe, lengthBits, 0, lengthBits.length);
+ Os.write(mPipe, msg, 0, msg.length);
+ }
+
+ EmulatorClipboardMonitor(final Consumer<ClipData> setAndroidClipboard) {
+ this.mHostMonitorThread = new Thread(() -> {
+ while (!Thread.interrupted()) {
+ try {
+ // There's no guarantee that QEMU pipes will be ready at the moment
+ // this method is invoked. We simply try to get the pipe open and
+ // retry on failure indefinitely.
+ while (!openPipe()) {
+ Thread.sleep(100);
+ }
+
+ final byte[] receivedData = receiveMessage();
+
+ final String str = new String(receivedData);
+ final ClipData clip = new ClipData("host clipboard",
+ new String[]{"text/plain"},
+ new ClipData.Item(str));
+
+ setAndroidClipboard.accept(clip);
+ } catch (ErrnoException | InterruptedIOException e) {
+ closePipe();
+ } catch (InterruptedException | IllegalArgumentException e) {
+ }
+ }
+ });
+
+ this.mHostMonitorThread.start();
+ }
+
+ @Override
+ public void accept(final @Nullable ClipData clip) {
+ if (clip == null) {
+ setHostClipboardImpl("");
+ } else if (clip.getItemCount() > 0) {
+ final CharSequence text = clip.getItemAt(0).getText();
+ if (text != null) {
+ setHostClipboardImpl(text.toString());
+ }
+ }
+ }
+
+ private void setHostClipboardImpl(final String value) {
+ try {
+ if (isPipeOpened()) {
+ sendMessage(value.getBytes());
+ }
+ } catch (ErrnoException | InterruptedIOException e) {
+ Slog.e(TAG, "Failed to set host clipboard " + e.getMessage());
+ } catch (IllegalArgumentException e) {
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/clipboard/OWNERS b/services/core/java/com/android/server/clipboard/OWNERS
new file mode 100644
index 0000000..5449df9
--- /dev/null
+++ b/services/core/java/com/android/server/clipboard/OWNERS
@@ -0,0 +1 @@
+per-file EmulatorClipboardMonitor.java = bohu@google.com,lfy@google.com,rkir@google.com
diff --git a/services/core/java/com/android/server/recoverysystem/RecoverySystemService.java b/services/core/java/com/android/server/recoverysystem/RecoverySystemService.java
index fe21201..6ea030f 100644
--- a/services/core/java/com/android/server/recoverysystem/RecoverySystemService.java
+++ b/services/core/java/com/android/server/recoverysystem/RecoverySystemService.java
@@ -16,6 +16,13 @@
package com.android.server.recoverysystem;
+import static android.os.RecoverySystem.RESUME_ON_REBOOT_REBOOT_ERROR_INVALID_PACKAGE_NAME;
+import static android.os.RecoverySystem.RESUME_ON_REBOOT_REBOOT_ERROR_LSKF_NOT_CAPTURED;
+import static android.os.RecoverySystem.RESUME_ON_REBOOT_REBOOT_ERROR_NONE;
+import static android.os.RecoverySystem.RESUME_ON_REBOOT_REBOOT_ERROR_PROVIDER_PREPARATION_FAILURE;
+import static android.os.RecoverySystem.RESUME_ON_REBOOT_REBOOT_ERROR_SLOT_MISMATCH;
+import static android.os.RecoverySystem.RESUME_ON_REBOOT_REBOOT_ERROR_UNSPECIFIED;
+import static android.os.RecoverySystem.ResumeOnRebootRebootErrorCode;
import static android.os.UserHandle.USER_SYSTEM;
import android.annotation.IntDef;
@@ -148,24 +155,6 @@
private @interface ResumeOnRebootActionsOnClear {}
/**
- * The error codes for reboots initiated by resume on reboot clients.
- */
- private static final int REBOOT_ERROR_NONE = 0;
- private static final int REBOOT_ERROR_UNKNOWN = 1;
- private static final int REBOOT_ERROR_INVALID_PACKAGE_NAME = 2;
- private static final int REBOOT_ERROR_LSKF_NOT_CAPTURED = 3;
- private static final int REBOOT_ERROR_SLOT_MISMATCH = 4;
- private static final int REBOOT_ERROR_ARM_REBOOT_ESCROW_FAILURE = 5;
-
- @IntDef({ REBOOT_ERROR_NONE,
- REBOOT_ERROR_UNKNOWN,
- REBOOT_ERROR_INVALID_PACKAGE_NAME,
- REBOOT_ERROR_LSKF_NOT_CAPTURED,
- REBOOT_ERROR_SLOT_MISMATCH,
- REBOOT_ERROR_ARM_REBOOT_ESCROW_FAILURE})
- private @interface ResumeOnRebootRebootErrorCode {}
-
- /**
* Manages shared preference, i.e. the storage used for metrics reporting.
*/
public static class PreferencesManager {
@@ -724,14 +713,14 @@
boolean slotSwitch) {
if (packageName == null) {
Slog.w(TAG, "Missing packageName when rebooting with lskf.");
- return REBOOT_ERROR_INVALID_PACKAGE_NAME;
+ return RESUME_ON_REBOOT_REBOOT_ERROR_INVALID_PACKAGE_NAME;
}
if (!isLskfCaptured(packageName)) {
- return REBOOT_ERROR_LSKF_NOT_CAPTURED;
+ return RESUME_ON_REBOOT_REBOOT_ERROR_LSKF_NOT_CAPTURED;
}
if (!verifySlotForNextBoot(slotSwitch)) {
- return REBOOT_ERROR_SLOT_MISMATCH;
+ return RESUME_ON_REBOOT_REBOOT_ERROR_SLOT_MISMATCH;
}
final long origId = Binder.clearCallingIdentity();
@@ -744,10 +733,10 @@
if (!result) {
Slog.w(TAG, "Failure to escrow key for reboot");
- return REBOOT_ERROR_ARM_REBOOT_ESCROW_FAILURE;
+ return RESUME_ON_REBOOT_REBOOT_ERROR_PROVIDER_PREPARATION_FAILURE;
}
- return REBOOT_ERROR_NONE;
+ return RESUME_ON_REBOOT_REBOOT_ERROR_NONE;
}
private boolean useServerBasedRoR() {
@@ -788,12 +777,13 @@
requestCount, slotSwitch, serverBased, durationSeconds, lskfCapturedCount);
}
- private boolean rebootWithLskfImpl(String packageName, String reason, boolean slotSwitch) {
+ private @ResumeOnRebootRebootErrorCode int rebootWithLskfImpl(String packageName, String reason,
+ boolean slotSwitch) {
@ResumeOnRebootRebootErrorCode int errorCode = armRebootEscrow(packageName, slotSwitch);
reportMetricsOnRebootWithLskf(packageName, slotSwitch, errorCode);
- if (errorCode != REBOOT_ERROR_NONE) {
- return false;
+ if (errorCode != RESUME_ON_REBOOT_REBOOT_ERROR_NONE) {
+ return errorCode;
}
// Clear the metrics prefs after a successful RoR reboot.
@@ -801,17 +791,19 @@
PowerManager pm = mInjector.getPowerManager();
pm.reboot(reason);
- return true;
+ return RESUME_ON_REBOOT_REBOOT_ERROR_UNSPECIFIED;
}
@Override // Binder call for the legacy rebootWithLskf
- public boolean rebootWithLskfAssumeSlotSwitch(String packageName, String reason) {
+ public @ResumeOnRebootRebootErrorCode int rebootWithLskfAssumeSlotSwitch(String packageName,
+ String reason) {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.RECOVERY, null);
return rebootWithLskfImpl(packageName, reason, true);
}
@Override // Binder call
- public boolean rebootWithLskf(String packageName, String reason, boolean slotSwitch) {
+ public @ResumeOnRebootRebootErrorCode int rebootWithLskf(String packageName, String reason,
+ boolean slotSwitch) {
enforcePermissionForResumeOnReboot();
return rebootWithLskfImpl(packageName, reason, slotSwitch);
}
diff --git a/services/core/java/com/android/server/recoverysystem/RecoverySystemShellCommand.java b/services/core/java/com/android/server/recoverysystem/RecoverySystemShellCommand.java
index ae71c1a..3d78828 100644
--- a/services/core/java/com/android/server/recoverysystem/RecoverySystemShellCommand.java
+++ b/services/core/java/com/android/server/recoverysystem/RecoverySystemShellCommand.java
@@ -17,6 +17,7 @@
package com.android.server.recoverysystem;
import android.os.IRecoverySystem;
+import android.os.RecoverySystem;
import android.os.RemoteException;
import android.os.ShellCommand;
@@ -76,7 +77,8 @@
private int rebootAndApply() throws RemoteException {
String packageName = getNextArgRequired();
String rebootReason = getNextArgRequired();
- boolean success = mService.rebootWithLskf(packageName, rebootReason, false);
+ boolean success = (mService.rebootWithLskf(packageName, rebootReason, false)
+ == RecoverySystem.RESUME_ON_REBOOT_REBOOT_ERROR_NONE);
PrintWriter pw = getOutPrintWriter();
// Keep the old message for cts test.
pw.printf("%s Reboot and apply status: %s\n", packageName,
diff --git a/services/core/java/com/android/server/vcn/Vcn.java b/services/core/java/com/android/server/vcn/Vcn.java
index ae806aa..7bc6056 100644
--- a/services/core/java/com/android/server/vcn/Vcn.java
+++ b/services/core/java/com/android/server/vcn/Vcn.java
@@ -38,6 +38,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.annotations.VisibleForTesting.Visibility;
+import com.android.internal.util.IndentingPrintWriter;
import com.android.server.VcnManagementService.VcnCallback;
import com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot;
@@ -328,6 +329,8 @@
private void handleNetworkRequested(
@NonNull NetworkRequest request, int score, int providerId) {
+ Slog.v(getLogTag(), "Received request " + request);
+
if (score > getNetworkScore()) {
if (VDBG) {
Slog.v(
@@ -409,6 +412,26 @@
return TAG + " [" + mSubscriptionGroup.hashCode() + "]";
}
+ /**
+ * Dumps the state of this Vcn for logging and debugging purposes.
+ *
+ * <p>PII and credentials MUST NEVER be dumped here.
+ */
+ public void dump(IndentingPrintWriter pw) {
+ pw.println("Vcn (" + mSubscriptionGroup + "):");
+ pw.increaseIndent();
+
+ pw.println("mCurrentStatus: " + mCurrentStatus);
+
+ pw.println("mVcnGatewayConnections:");
+ for (VcnGatewayConnection gw : mVcnGatewayConnections.values()) {
+ gw.dump(pw);
+ }
+ pw.println();
+
+ pw.decreaseIndent();
+ }
+
/** Retrieves the network score for a VCN Network */
// Package visibility for use in VcnGatewayConnection
static int getNetworkScore() {
diff --git a/services/core/java/com/android/server/vcn/VcnGatewayConnection.java b/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
index 20c08eb..83ac36f 100644
--- a/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
+++ b/services/core/java/com/android/server/vcn/VcnGatewayConnection.java
@@ -77,6 +77,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.annotations.VisibleForTesting.Visibility;
+import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.State;
import com.android.internal.util.StateMachine;
import com.android.internal.util.WakeupMessage;
@@ -84,6 +85,7 @@
import com.android.server.vcn.UnderlyingNetworkTracker.UnderlyingNetworkRecord;
import com.android.server.vcn.UnderlyingNetworkTracker.UnderlyingNetworkTrackerCallback;
import com.android.server.vcn.Vcn.VcnGatewayStatusCallback;
+import com.android.server.vcn.util.MtuUtils;
import java.io.IOException;
import java.net.Inet4Address;
@@ -448,6 +450,44 @@
*/
private static final int EVENT_SAFE_MODE_TIMEOUT_EXCEEDED = 10;
+ /**
+ * Sent when an IKE has completed migration, and created updated transforms for application.
+ *
+ * <p>Only relevant in the Connected state.
+ *
+ * @param arg1 The session token for the IKE Session that completed migration, used to prevent
+ * out-of-date signals from propagating.
+ * @param obj @NonNull An EventMigrationCompletedInfo instance with relevant data.
+ */
+ private static final int EVENT_MIGRATION_COMPLETED = 11;
+
+ private static class EventMigrationCompletedInfo implements EventInfo {
+ @NonNull public final IpSecTransform inTransform;
+ @NonNull public final IpSecTransform outTransform;
+
+ EventMigrationCompletedInfo(
+ @NonNull IpSecTransform inTransform, @NonNull IpSecTransform outTransform) {
+ this.inTransform = Objects.requireNonNull(inTransform);
+ this.outTransform = Objects.requireNonNull(outTransform);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(inTransform, outTransform);
+ }
+
+ @Override
+ public boolean equals(@Nullable Object other) {
+ if (!(other instanceof EventMigrationCompletedInfo)) {
+ return false;
+ }
+
+ final EventMigrationCompletedInfo rhs = (EventMigrationCompletedInfo) other;
+ return Objects.equals(inTransform, rhs.inTransform)
+ && Objects.equals(outTransform, rhs.outTransform);
+ }
+ }
+
@VisibleForTesting(visibility = Visibility.PRIVATE)
@NonNull
final DisconnectedState mDisconnectedState = new DisconnectedState();
@@ -574,7 +614,7 @@
* <p>Set in Connected state, always @NonNull in Connected, Migrating states, @Nullable
* otherwise.
*/
- private NetworkAgent mNetworkAgent;
+ private VcnNetworkAgent mNetworkAgent;
@Nullable private WakeupMessage mTeardownTimeoutAlarm;
@Nullable private WakeupMessage mDisconnectRequestAlarm;
@@ -1053,6 +1093,14 @@
sendMessageAndAcquireWakeLock(EVENT_SESSION_CLOSED, token);
}
+ private void migrationCompleted(
+ int token, @NonNull IpSecTransform inTransform, @NonNull IpSecTransform outTransform) {
+ sendMessageAndAcquireWakeLock(
+ EVENT_MIGRATION_COMPLETED,
+ token,
+ new EventMigrationCompletedInfo(inTransform, outTransform));
+ }
+
private void childTransformCreated(
int token, @NonNull IpSecTransform transform, int direction) {
sendMessageAndAcquireWakeLock(
@@ -1148,7 +1196,9 @@
case EVENT_SETUP_COMPLETED: // Fallthrough
case EVENT_DISCONNECT_REQUESTED: // Fallthrough
case EVENT_TEARDOWN_TIMEOUT_EXPIRED: // Fallthrough
- case EVENT_SUBSCRIPTIONS_CHANGED:
+ case EVENT_SUBSCRIPTIONS_CHANGED: // Fallthrough
+ case EVENT_SAFE_MODE_TIMEOUT_EXCEEDED: // Fallthrough
+ case EVENT_MIGRATION_COMPLETED:
logUnexpectedEvent(msg.what);
break;
default:
@@ -1440,30 +1490,32 @@
private abstract class ConnectedStateBase extends ActiveBaseState {
protected void updateNetworkAgent(
@NonNull IpSecTunnelInterface tunnelIface,
- @NonNull NetworkAgent agent,
+ @NonNull VcnNetworkAgent agent,
@NonNull VcnChildSessionConfiguration childConfig) {
final NetworkCapabilities caps =
buildNetworkCapabilities(mConnectionConfig, mUnderlying);
final LinkProperties lp =
- buildConnectedLinkProperties(mConnectionConfig, tunnelIface, childConfig);
+ buildConnectedLinkProperties(
+ mConnectionConfig, tunnelIface, childConfig, mUnderlying);
agent.sendNetworkCapabilities(caps);
agent.sendLinkProperties(lp);
}
- protected NetworkAgent buildNetworkAgent(
+ protected VcnNetworkAgent buildNetworkAgent(
@NonNull IpSecTunnelInterface tunnelIface,
@NonNull VcnChildSessionConfiguration childConfig) {
final NetworkCapabilities caps =
buildNetworkCapabilities(mConnectionConfig, mUnderlying);
final LinkProperties lp =
- buildConnectedLinkProperties(mConnectionConfig, tunnelIface, childConfig);
+ buildConnectedLinkProperties(
+ mConnectionConfig, tunnelIface, childConfig, mUnderlying);
final NetworkAgentConfig nac =
new NetworkAgentConfig.Builder()
.setLegacyType(ConnectivityManager.TYPE_MOBILE)
.build();
- final NetworkAgent agent =
+ final VcnNetworkAgent agent =
mDeps.newNetworkAgent(
mVcnContext,
TAG,
@@ -1472,15 +1524,21 @@
Vcn.getNetworkScore(),
nac,
mVcnContext.getVcnNetworkProvider(),
- () -> {
- Slog.d(TAG, "NetworkAgent was unwanted");
- // If network agent has already been torn down, skip sending the
- // disconnect. Unwanted() is always called, even when networkAgents
- // are unregistered in teardownNetwork(), so prevent duplicate
- // notifications.
- if (mNetworkAgent != null) {
- teardownAsynchronously();
+ (agentRef) -> {
+ // Only trigger teardown if the NetworkAgent hasn't been replaced or
+ // changed. This guards against two cases - the first where
+ // unwanted() may be called as a result of the
+ // NetworkAgent.unregister() call, which might trigger a teardown
+ // instead of just a Network disconnect, as well as the case where a
+ // new NetworkAgent replaces an old one before the unwanted() call
+ // is processed.
+ if (mNetworkAgent != agentRef) {
+ Slog.d(TAG, "unwanted() called on stale NetworkAgent");
+ return;
}
+
+ Slog.d(TAG, "NetworkAgent was unwanted");
+ teardownAsynchronously();
} /* networkUnwantedCallback */,
(status) -> {
if (status == NetworkAgent.VALIDATION_STATUS_VALID) {
@@ -1620,12 +1678,36 @@
case EVENT_SAFE_MODE_TIMEOUT_EXCEEDED:
handleSafeModeTimeoutExceeded();
break;
+ case EVENT_MIGRATION_COMPLETED:
+ final EventMigrationCompletedInfo migrationCompletedInfo =
+ (EventMigrationCompletedInfo) msg.obj;
+
+ handleMigrationCompleted(migrationCompletedInfo);
+ break;
default:
logUnhandledMessage(msg);
break;
}
}
+ private void handleMigrationCompleted(EventMigrationCompletedInfo migrationCompletedInfo) {
+ applyTransform(
+ mCurrentToken,
+ mTunnelIface,
+ mUnderlying.network,
+ migrationCompletedInfo.inTransform,
+ IpSecManager.DIRECTION_IN);
+
+ applyTransform(
+ mCurrentToken,
+ mTunnelIface,
+ mUnderlying.network,
+ migrationCompletedInfo.outTransform,
+ IpSecManager.DIRECTION_OUT);
+
+ updateNetworkAgent(mTunnelIface, mNetworkAgent, mChildConfig);
+ }
+
private void handleUnderlyingNetworkChanged(@NonNull Message msg) {
final UnderlyingNetworkRecord oldUnderlying = mUnderlying;
mUnderlying = ((EventUnderlyingNetworkChangedInfo) msg.obj).newUnderlying;
@@ -1815,7 +1897,10 @@
private static LinkProperties buildConnectedLinkProperties(
@NonNull VcnGatewayConnectionConfig gatewayConnectionConfig,
@NonNull IpSecTunnelInterface tunnelIface,
- @NonNull VcnChildSessionConfiguration childConfig) {
+ @NonNull VcnChildSessionConfiguration childConfig,
+ @Nullable UnderlyingNetworkRecord underlying) {
+ final VcnControlPlaneIkeConfig controlPlaneConfig =
+ (VcnControlPlaneIkeConfig) gatewayConnectionConfig.getControlPlaneConfig();
final LinkProperties lp = new LinkProperties();
lp.setInterfaceName(tunnelIface.getInterfaceName());
@@ -1831,7 +1916,12 @@
lp.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), null /*gateway*/,
null /*iface*/, RouteInfo.RTN_UNICAST));
- lp.setMtu(gatewayConnectionConfig.getMaxMtu());
+ final int underlyingMtu = (underlying == null) ? 0 : underlying.linkProperties.getMtu();
+ lp.setMtu(
+ MtuUtils.getMtu(
+ controlPlaneConfig.getChildSessionParams().getSaProposals(),
+ gatewayConnectionConfig.getMaxMtu(),
+ underlyingMtu));
return lp;
}
@@ -1912,8 +2002,7 @@
@NonNull IpSecTransform inIpSecTransform,
@NonNull IpSecTransform outIpSecTransform) {
Slog.v(TAG, "ChildTransformsMigrated; token " + mToken);
- onIpSecTransformCreated(inIpSecTransform, IpSecManager.DIRECTION_IN);
- onIpSecTransformCreated(outIpSecTransform, IpSecManager.DIRECTION_OUT);
+ migrationCompleted(mToken, inIpSecTransform, outIpSecTransform);
}
@Override
@@ -1924,6 +2013,27 @@
}
}
+ /**
+ * Dumps the state of this VcnGatewayConnection for logging and debugging purposes.
+ *
+ * <p>PII and credentials MUST NEVER be dumped here.
+ */
+ public void dump(IndentingPrintWriter pw) {
+ pw.println("VcnGatewayConnection (" + mConnectionConfig.getGatewayConnectionName() + "):");
+ pw.increaseIndent();
+
+ pw.println("Current state: " + getCurrentState().getClass().getSimpleName());
+ pw.println("mIsQuitting: " + mIsQuitting);
+ pw.println("mIsInSafeMode: " + mIsInSafeMode);
+ pw.println("mCurrentToken: " + mCurrentToken);
+ pw.println("mFailedAttempts: " + mFailedAttempts);
+ pw.println(
+ "mNetworkAgent.getNetwork(): "
+ + (mNetworkAgent == null ? null : mNetworkAgent.getNetwork()));
+
+ pw.decreaseIndent();
+ }
+
@VisibleForTesting(visibility = Visibility.PRIVATE)
void setTunnelInterface(IpSecTunnelInterface tunnelIface) {
mTunnelIface = tunnelIface;
@@ -1965,12 +2075,12 @@
}
@VisibleForTesting(visibility = Visibility.PRIVATE)
- NetworkAgent getNetworkAgent() {
+ VcnNetworkAgent getNetworkAgent() {
return mNetworkAgent;
}
@VisibleForTesting(visibility = Visibility.PRIVATE)
- void setNetworkAgent(@Nullable NetworkAgent networkAgent) {
+ void setNetworkAgent(@Nullable VcnNetworkAgent networkAgent) {
mNetworkAgent = networkAgent;
}
@@ -2058,8 +2168,8 @@
return new WakeupMessage(vcnContext.getContext(), handler, tag, runnable);
}
- /** Builds a new NetworkAgent. */
- public NetworkAgent newNetworkAgent(
+ /** Builds a new VcnNetworkAgent. */
+ public VcnNetworkAgent newNetworkAgent(
@NonNull VcnContext vcnContext,
@NonNull String tag,
@NonNull NetworkCapabilities caps,
@@ -2067,27 +2177,18 @@
@NonNull int score,
@NonNull NetworkAgentConfig nac,
@NonNull NetworkProvider provider,
- @NonNull Runnable networkUnwantedCallback,
+ @NonNull Consumer<VcnNetworkAgent> networkUnwantedCallback,
@NonNull Consumer<Integer> validationStatusCallback) {
- return new NetworkAgent(
- vcnContext.getContext(),
- vcnContext.getLooper(),
+ return new VcnNetworkAgent(
+ vcnContext,
tag,
caps,
lp,
score,
nac,
- provider) {
- @Override
- public void onNetworkUnwanted() {
- networkUnwantedCallback.run();
- }
-
- @Override
- public void onValidationStatus(int status, @Nullable Uri redirectUri) {
- validationStatusCallback.accept(status);
- }
- };
+ provider,
+ networkUnwantedCallback,
+ validationStatusCallback);
}
/** Gets the elapsed real time since boot, in millis. */
@@ -2203,4 +2304,73 @@
mImpl.release();
}
}
+
+ /** Proxy Implementation of NetworkAgent, used for testing. */
+ @VisibleForTesting(visibility = Visibility.PRIVATE)
+ public static class VcnNetworkAgent {
+ private final NetworkAgent mImpl;
+
+ public VcnNetworkAgent(
+ @NonNull VcnContext vcnContext,
+ @NonNull String tag,
+ @NonNull NetworkCapabilities caps,
+ @NonNull LinkProperties lp,
+ @NonNull int score,
+ @NonNull NetworkAgentConfig nac,
+ @NonNull NetworkProvider provider,
+ @NonNull Consumer<VcnNetworkAgent> networkUnwantedCallback,
+ @NonNull Consumer<Integer> validationStatusCallback) {
+ mImpl =
+ new NetworkAgent(
+ vcnContext.getContext(),
+ vcnContext.getLooper(),
+ tag,
+ caps,
+ lp,
+ score,
+ nac,
+ provider) {
+ @Override
+ public void onNetworkUnwanted() {
+ networkUnwantedCallback.accept(VcnNetworkAgent.this);
+ }
+
+ @Override
+ public void onValidationStatus(int status, @Nullable Uri redirectUri) {
+ validationStatusCallback.accept(status);
+ }
+ };
+ }
+
+ /** Registers the underlying NetworkAgent */
+ public void register() {
+ mImpl.register();
+ }
+
+ /** Marks the underlying NetworkAgent as connected */
+ public void markConnected() {
+ mImpl.markConnected();
+ }
+
+ /** Unregisters the underlying NetworkAgent */
+ public void unregister() {
+ mImpl.unregister();
+ }
+
+ /** Sends new NetworkCapabilities for the underlying NetworkAgent */
+ public void sendNetworkCapabilities(@NonNull NetworkCapabilities caps) {
+ mImpl.sendNetworkCapabilities(caps);
+ }
+
+ /** Sends new LinkProperties for the underlying NetworkAgent */
+ public void sendLinkProperties(@NonNull LinkProperties lp) {
+ mImpl.sendLinkProperties(lp);
+ }
+
+ /** Retrieves the Network for the underlying NetworkAgent */
+ @Nullable
+ public Network getNetwork() {
+ return mImpl.getNetwork();
+ }
+ }
}
diff --git a/services/core/java/com/android/server/vcn/VcnNetworkProvider.java b/services/core/java/com/android/server/vcn/VcnNetworkProvider.java
index a909695..be0deb5 100644
--- a/services/core/java/com/android/server/vcn/VcnNetworkProvider.java
+++ b/services/core/java/com/android/server/vcn/VcnNetworkProvider.java
@@ -29,6 +29,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.annotations.VisibleForTesting.Visibility;
+import com.android.internal.util.IndentingPrintWriter;
import java.util.Objects;
import java.util.Set;
@@ -129,10 +130,50 @@
mScore = score;
mProviderId = providerId;
}
+
+ /**
+ * Dumps the state of this NetworkRequestEntry for logging and debugging purposes.
+ *
+ * <p>PII and credentials MUST NEVER be dumped here.
+ */
+ public void dump(IndentingPrintWriter pw) {
+ pw.println("NetworkRequestEntry:");
+ pw.increaseIndent();
+
+ pw.println("mRequest: " + mRequest);
+ pw.println("mScore: " + mScore);
+ pw.println("mProviderId: " + mProviderId);
+
+ pw.decreaseIndent();
+ }
}
// package-private
interface NetworkRequestListener {
void onNetworkRequested(@NonNull NetworkRequest request, int score, int providerId);
}
+
+ /**
+ * Dumps the state of this VcnNetworkProvider for logging and debugging purposes.
+ *
+ * <p>PII and credentials MUST NEVER be dumped here.
+ */
+ public void dump(IndentingPrintWriter pw) {
+ pw.println("VcnNetworkProvider:");
+ pw.increaseIndent();
+
+ pw.println("mListeners:");
+ for (NetworkRequestListener listener : mListeners) {
+ pw.println(listener);
+ }
+ pw.println();
+
+ pw.println("mRequests.values:");
+ for (NetworkRequestEntry entry : mRequests.values()) {
+ entry.dump(pw);
+ }
+ pw.println();
+
+ pw.decreaseIndent();
+ }
}
diff --git a/services/core/java/com/android/server/vcn/util/MtuUtils.java b/services/core/java/com/android/server/vcn/util/MtuUtils.java
new file mode 100644
index 0000000..49c1a02
--- /dev/null
+++ b/services/core/java/com/android/server/vcn/util/MtuUtils.java
@@ -0,0 +1,155 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.vcn.util;
+
+import static android.net.ipsec.ike.SaProposal.ENCRYPTION_ALGORITHM_3DES;
+import static android.net.ipsec.ike.SaProposal.ENCRYPTION_ALGORITHM_AES_CBC;
+import static android.net.ipsec.ike.SaProposal.ENCRYPTION_ALGORITHM_AES_CTR;
+import static android.net.ipsec.ike.SaProposal.ENCRYPTION_ALGORITHM_AES_GCM_12;
+import static android.net.ipsec.ike.SaProposal.ENCRYPTION_ALGORITHM_AES_GCM_16;
+import static android.net.ipsec.ike.SaProposal.ENCRYPTION_ALGORITHM_AES_GCM_8;
+import static android.net.ipsec.ike.SaProposal.ENCRYPTION_ALGORITHM_CHACHA20_POLY1305;
+import static android.net.ipsec.ike.SaProposal.INTEGRITY_ALGORITHM_AES_CMAC_96;
+import static android.net.ipsec.ike.SaProposal.INTEGRITY_ALGORITHM_AES_XCBC_96;
+import static android.net.ipsec.ike.SaProposal.INTEGRITY_ALGORITHM_HMAC_SHA1_96;
+import static android.net.ipsec.ike.SaProposal.INTEGRITY_ALGORITHM_HMAC_SHA2_256_128;
+import static android.net.ipsec.ike.SaProposal.INTEGRITY_ALGORITHM_HMAC_SHA2_384_192;
+import static android.net.ipsec.ike.SaProposal.INTEGRITY_ALGORITHM_HMAC_SHA2_512_256;
+import static android.net.ipsec.ike.SaProposal.INTEGRITY_ALGORITHM_NONE;
+
+import static com.android.net.module.util.NetworkStackConstants.IPV6_MIN_MTU;
+
+import static java.lang.Math.max;
+import static java.util.Collections.unmodifiableMap;
+
+import android.annotation.NonNull;
+import android.net.ipsec.ike.ChildSaProposal;
+import android.util.ArrayMap;
+import android.util.Pair;
+import android.util.Slog;
+
+import java.util.List;
+import java.util.Map;
+
+/** @hide */
+public class MtuUtils {
+ private static final String TAG = MtuUtils.class.getSimpleName();
+ /**
+ * Max ESP overhead possible
+ *
+ * <p>60 (Outer IPv4 + options) + 8 (UDP encap) + 4 (SPI) + 4 (Seq) + 2 (Pad + NextHeader)
+ */
+ private static final int GENERIC_ESP_OVERHEAD_MAX = 78;
+
+ /** Maximum overheads of authentication algorithms, keyed on IANA-defined constants */
+ private static final Map<Integer, Integer> AUTH_ALGORITHM_OVERHEAD;
+
+ static {
+ final Map<Integer, Integer> map = new ArrayMap<>();
+ map.put(INTEGRITY_ALGORITHM_NONE, 0);
+ map.put(INTEGRITY_ALGORITHM_HMAC_SHA1_96, 12);
+ map.put(INTEGRITY_ALGORITHM_AES_XCBC_96, 12);
+ map.put(INTEGRITY_ALGORITHM_HMAC_SHA2_256_128, 32);
+ map.put(INTEGRITY_ALGORITHM_HMAC_SHA2_384_192, 48);
+ map.put(INTEGRITY_ALGORITHM_HMAC_SHA2_512_256, 64);
+ map.put(INTEGRITY_ALGORITHM_AES_CMAC_96, 12);
+
+ AUTH_ALGORITHM_OVERHEAD = unmodifiableMap(map);
+ }
+
+ /** Maximum overheads of encryption algorithms, keyed on IANA-defined constants */
+ private static final Map<Integer, Integer> CRYPT_ALGORITHM_OVERHEAD;
+
+ static {
+ final Map<Integer, Integer> map = new ArrayMap<>();
+ map.put(ENCRYPTION_ALGORITHM_3DES, 15); // 8 (IV) + 7 (Max pad)
+ map.put(ENCRYPTION_ALGORITHM_AES_CBC, 31); // 16 (IV) + 15 (Max pad)
+ map.put(ENCRYPTION_ALGORITHM_AES_CTR, 11); // 8 (IV) + 3 (Max pad)
+
+ CRYPT_ALGORITHM_OVERHEAD = unmodifiableMap(map);
+ }
+
+ /** Maximum overheads of combined mode algorithms, keyed on IANA-defined constants */
+ private static final Map<Integer, Integer> AUTHCRYPT_ALGORITHM_OVERHEAD;
+
+ static {
+ final Map<Integer, Integer> map = new ArrayMap<>();
+ map.put(ENCRYPTION_ALGORITHM_AES_GCM_8, 19); // 8 (IV) + 3 (Max pad) + 8 (ICV)
+ map.put(ENCRYPTION_ALGORITHM_AES_GCM_12, 23); // 8 (IV) + 3 (Max pad) + 12 (ICV)
+ map.put(ENCRYPTION_ALGORITHM_AES_GCM_16, 27); // 8 (IV) + 3 (Max pad) + 16 (ICV)
+ map.put(ENCRYPTION_ALGORITHM_CHACHA20_POLY1305, 27); // 8 (IV) + 3 (Max pad) + 16 (ICV)
+
+ AUTHCRYPT_ALGORITHM_OVERHEAD = unmodifiableMap(map);
+ }
+
+ /**
+ * Calculates the MTU of the inner interface based on the parameters provided
+ *
+ * <p>The MTU of the inner interface will be the minimum of the following:
+ *
+ * <ul>
+ * <li>The MTU of the outer interface, minus the greatest ESP overhead (based on proposed
+ * algorithms).
+ * <li>The maximum MTU as provided in the arguments.
+ * </ul>
+ */
+ public static int getMtu(
+ @NonNull List<ChildSaProposal> childProposals, int maxMtu, int underlyingMtu) {
+ if (underlyingMtu <= 0) {
+ return IPV6_MIN_MTU;
+ }
+
+ boolean hasUnknownAlgorithm = false;
+ int maxAuthOverhead = 0;
+ int maxCryptOverhead = 0;
+ int maxAuthCryptOverhead = 0;
+
+ for (ChildSaProposal proposal : childProposals) {
+ for (Pair<Integer, Integer> encryptionAlgoPair : proposal.getEncryptionAlgorithms()) {
+ final int algo = encryptionAlgoPair.first;
+
+ if (AUTHCRYPT_ALGORITHM_OVERHEAD.containsKey(algo)) {
+ maxAuthCryptOverhead =
+ max(maxAuthCryptOverhead, AUTHCRYPT_ALGORITHM_OVERHEAD.get(algo));
+ continue;
+ } else if (CRYPT_ALGORITHM_OVERHEAD.containsKey(algo)) {
+ maxCryptOverhead = max(maxCryptOverhead, CRYPT_ALGORITHM_OVERHEAD.get(algo));
+ continue;
+ }
+
+ Slog.wtf(TAG, "Unknown encryption algorithm requested: " + algo);
+ return IPV6_MIN_MTU;
+ }
+
+ for (int algo : proposal.getIntegrityAlgorithms()) {
+ if (AUTH_ALGORITHM_OVERHEAD.containsKey(algo)) {
+ maxAuthOverhead = max(maxAuthOverhead, AUTH_ALGORITHM_OVERHEAD.get(algo));
+ continue;
+ }
+
+ Slog.wtf(TAG, "Unknown integrity algorithm requested: " + algo);
+ return IPV6_MIN_MTU;
+ }
+ }
+
+ // Return minimum of maxMtu, and the adjusted MTUs based on algorithms.
+ final int combinedModeMtu = underlyingMtu - maxAuthCryptOverhead - GENERIC_ESP_OVERHEAD_MAX;
+ final int normalModeMtu =
+ underlyingMtu - maxCryptOverhead - maxAuthOverhead - GENERIC_ESP_OVERHEAD_MAX;
+ return Math.min(Math.min(maxMtu, combinedModeMtu), normalModeMtu);
+ }
+}
diff --git a/services/net/java/android/net/util/KeepalivePacketDataUtil.java b/services/net/java/android/net/util/KeepalivePacketDataUtil.java
index 6e539bb..5666985 100644
--- a/services/net/java/android/net/util/KeepalivePacketDataUtil.java
+++ b/services/net/java/android/net/util/KeepalivePacketDataUtil.java
@@ -80,12 +80,12 @@
parcel.srcPort = pkt.getSrcPort();
parcel.dstAddress = dstAddress.getAddress();
parcel.dstPort = pkt.getDstPort();
- parcel.seq = pkt.tcpSeq;
- parcel.ack = pkt.tcpAck;
- parcel.rcvWnd = pkt.tcpWindow;
- parcel.rcvWndScale = pkt.tcpWindowScale;
- parcel.tos = pkt.ipTos;
- parcel.ttl = pkt.ipTtl;
+ parcel.seq = pkt.getTcpSeq();
+ parcel.ack = pkt.getTcpAck();
+ parcel.rcvWnd = pkt.getTcpWindow();
+ parcel.rcvWndScale = pkt.getTcpWindowScale();
+ parcel.tos = pkt.getIpTos();
+ parcel.ttl = pkt.getIpTtl();
return parcel;
}
diff --git a/services/tests/servicestests/src/com/android/server/recoverysystem/RecoverySystemServiceTest.java b/services/tests/servicestests/src/com/android/server/recoverysystem/RecoverySystemServiceTest.java
index 7903a90..2b358ea 100644
--- a/services/tests/servicestests/src/com/android/server/recoverysystem/RecoverySystemServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/recoverysystem/RecoverySystemServiceTest.java
@@ -16,7 +16,12 @@
package com.android.server.recoverysystem;
+import static android.os.RecoverySystem.RESUME_ON_REBOOT_REBOOT_ERROR_INVALID_PACKAGE_NAME;
+import static android.os.RecoverySystem.RESUME_ON_REBOOT_REBOOT_ERROR_LSKF_NOT_CAPTURED;
+import static android.os.RecoverySystem.RESUME_ON_REBOOT_REBOOT_ERROR_SLOT_MISMATCH;
+
import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat;
import static org.mockito.AdditionalMatchers.not;
import static org.mockito.ArgumentMatchers.any;
@@ -274,8 +279,7 @@
verify(intentSender).sendIntent(any(), anyInt(), any(), any(), any());
assertThat(mRecoverySystemService.requestLskf(FAKE_OTA_PACKAGE_NAME, null), is(true));
- assertThat(mRecoverySystemService.rebootWithLskf(FAKE_OTA_PACKAGE_NAME, "foobar", true),
- is(true));
+ mRecoverySystemService.rebootWithLskf(FAKE_OTA_PACKAGE_NAME, "foobar", true);
verify(mIPowerManager).reboot(anyBoolean(), eq("foobar"), anyBoolean());
}
@@ -373,8 +377,7 @@
anyInt())).thenReturn(3);
when(mSharedPreferences.getLong(eq(RecoverySystemService.LSKF_CAPTURED_TIMESTAMP_PREF),
anyLong())).thenReturn(40_000L);
- assertThat(mRecoverySystemService.rebootWithLskf(FAKE_OTA_PACKAGE_NAME, "ab-update", true),
- is(true));
+ mRecoverySystemService.rebootWithLskf(FAKE_OTA_PACKAGE_NAME, "ab-update", true);
verify(mIPowerManager).reboot(anyBoolean(), eq("ab-update"), anyBoolean());
verify(mMetricsReporter).reportRebootEscrowRebootMetrics(eq(0), eq(1000),
eq(1) /* client count */, eq(2) /* request count */, eq(true) /* slot switch */,
@@ -386,19 +389,20 @@
public void rebootWithLskf_slotMismatch_Failure() throws Exception {
assertThat(mRecoverySystemService.requestLskf(FAKE_OTA_PACKAGE_NAME, null), is(true));
mRecoverySystemService.onPreparedForReboot(true);
- assertThat(mRecoverySystemService.rebootWithLskf(FAKE_OTA_PACKAGE_NAME, "ab-update", false),
- is(false));
+ assertEquals(RESUME_ON_REBOOT_REBOOT_ERROR_SLOT_MISMATCH,
+ mRecoverySystemService.rebootWithLskf(FAKE_OTA_PACKAGE_NAME, "ab-update", false));
}
@Test
public void rebootWithLskf_withoutPrepare_Failure() throws Exception {
- assertThat(mRecoverySystemService.rebootWithLskf(FAKE_OTA_PACKAGE_NAME, null, true),
- is(false));
+ assertEquals(RESUME_ON_REBOOT_REBOOT_ERROR_LSKF_NOT_CAPTURED,
+ mRecoverySystemService.rebootWithLskf(FAKE_OTA_PACKAGE_NAME, null, true));
}
@Test
public void rebootWithLskf_withNullCallerId_Failure() throws Exception {
- assertThat(mRecoverySystemService.rebootWithLskf(null, null, true), is(false));
+ assertEquals(RESUME_ON_REBOOT_REBOOT_ERROR_INVALID_PACKAGE_NAME,
+ mRecoverySystemService.rebootWithLskf(null, null, true));
verifyNoMoreInteractions(mIPowerManager);
}
@@ -410,8 +414,7 @@
// Client B's clear won't affect client A's preparation.
assertThat(mRecoverySystemService.clearLskf(FAKE_OTHER_PACKAGE_NAME), is(true));
- assertThat(mRecoverySystemService.rebootWithLskf(FAKE_OTA_PACKAGE_NAME, "ab-update", true),
- is(true));
+ mRecoverySystemService.rebootWithLskf(FAKE_OTA_PACKAGE_NAME, "ab-update", true);
verify(mIPowerManager).reboot(anyBoolean(), eq("ab-update"), anyBoolean());
}
@@ -428,8 +431,7 @@
when(mSharedPreferences.getLong(eq(RecoverySystemService.LSKF_CAPTURED_TIMESTAMP_PREF),
anyLong())).thenReturn(60_000L);
- assertThat(mRecoverySystemService.rebootWithLskf(FAKE_OTA_PACKAGE_NAME, "ab-update", true),
- is(true));
+ mRecoverySystemService.rebootWithLskf(FAKE_OTA_PACKAGE_NAME, "ab-update", true);
verify(mIPowerManager).reboot(anyBoolean(), eq("ab-update"), anyBoolean());
verify(mMetricsReporter).reportRebootEscrowRebootMetrics(eq(0), eq(1000),
eq(2) /* client count */, eq(2) /* request count */, eq(true) /* slot switch */,
@@ -450,17 +452,15 @@
anyLong())).thenReturn(60_000L);
assertThat(mRecoverySystemService.clearLskf(FAKE_OTA_PACKAGE_NAME), is(true));
- assertThat(mRecoverySystemService.rebootWithLskf(FAKE_OTA_PACKAGE_NAME, null, true),
- is(false));
+ assertEquals(RESUME_ON_REBOOT_REBOOT_ERROR_LSKF_NOT_CAPTURED,
+ mRecoverySystemService.rebootWithLskf(FAKE_OTA_PACKAGE_NAME, null, true));
verifyNoMoreInteractions(mIPowerManager);
verify(mMetricsReporter).reportRebootEscrowRebootMetrics(not(eq(0)), eq(1000),
eq(1) /* client count */, anyInt() /* request count */, eq(true) /* slot switch */,
anyBoolean(), eq(40), eq(1)/* lskf capture count */);
assertThat(mRecoverySystemService.requestLskf(FAKE_OTHER_PACKAGE_NAME, null), is(true));
- assertThat(
- mRecoverySystemService.rebootWithLskf(FAKE_OTHER_PACKAGE_NAME, "ab-update", true),
- is(true));
+ mRecoverySystemService.rebootWithLskf(FAKE_OTHER_PACKAGE_NAME, "ab-update", true);
verify(mIPowerManager).reboot(anyBoolean(), eq("ab-update"), anyBoolean());
verify(mMetricsReporter).reportRebootEscrowRebootMetrics((eq(0)), eq(2000),
@@ -476,16 +476,15 @@
// Client A clears
assertThat(mRecoverySystemService.clearLskf(FAKE_OTA_PACKAGE_NAME), is(true));
- assertThat(mRecoverySystemService.rebootWithLskf(FAKE_OTA_PACKAGE_NAME, null, true),
- is(false));
+ assertEquals(RESUME_ON_REBOOT_REBOOT_ERROR_LSKF_NOT_CAPTURED,
+ mRecoverySystemService.rebootWithLskf(FAKE_OTA_PACKAGE_NAME, null, true));
verifyNoMoreInteractions(mIPowerManager);
// Client B clears
assertThat(mRecoverySystemService.clearLskf(FAKE_OTHER_PACKAGE_NAME), is(true));
verify(mLockSettingsInternal).clearRebootEscrow();
- assertThat(
- mRecoverySystemService.rebootWithLskf(FAKE_OTHER_PACKAGE_NAME, "ab-update", true),
- is(false));
+ assertEquals(RESUME_ON_REBOOT_REBOOT_ERROR_LSKF_NOT_CAPTURED,
+ mRecoverySystemService.rebootWithLskf(FAKE_OTHER_PACKAGE_NAME, "ab-update", true));
verifyNoMoreInteractions(mIPowerManager);
}
diff --git a/telephony/common/com/android/internal/telephony/TelephonyPermissions.java b/telephony/common/com/android/internal/telephony/TelephonyPermissions.java
index 38fa907..3c3df51 100644
--- a/telephony/common/com/android/internal/telephony/TelephonyPermissions.java
+++ b/telephony/common/com/android/internal/telephony/TelephonyPermissions.java
@@ -525,7 +525,7 @@
*
* @throws SecurityException if the caller does not have the required permission/privileges
*/
- public static void enforeceCallingOrSelfReadPhoneStatePermissionOrCarrierPrivilege(
+ public static void enforceCallingOrSelfReadPhoneStatePermissionOrCarrierPrivilege(
Context context, int subId, String message) {
if (context.checkCallingOrSelfPermission(android.Manifest.permission.READ_PHONE_STATE)
== PERMISSION_GRANTED) {
@@ -545,7 +545,7 @@
*
* @throws SecurityException if the caller does not have the required permission/privileges
*/
- public static void enforeceCallingOrSelfReadPrivilegedPhoneStatePermissionOrCarrierPrivilege(
+ public static void enforceCallingOrSelfReadPrivilegedPhoneStatePermissionOrCarrierPrivilege(
Context context, int subId, String message) {
if (context.checkCallingOrSelfPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
== PERMISSION_GRANTED) {
@@ -567,7 +567,7 @@
*
* @throws SecurityException if the caller does not have the required permission/privileges
*/
- public static void enforeceCallingOrSelfReadPrecisePhoneStatePermissionOrCarrierPrivilege(
+ public static void enforceCallingOrSelfReadPrecisePhoneStatePermissionOrCarrierPrivilege(
Context context, int subId, String message) {
if (context.checkCallingOrSelfPermission(Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
== PERMISSION_GRANTED) {
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index f027cd1..073f1e3 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -3222,6 +3222,14 @@
public static final String KEY_USE_CALLER_ID_USSD_BOOL = "use_caller_id_ussd_bool";
/**
+ * Call waiting uses USSD command without SS command.
+ * When {@code true}, the call waiting query/set by ussd command.
+ * When {@code false}, doesn't use USSD to query/set call waiting.
+ * @hide
+ */
+ public static final String KEY_USE_CALL_WAITING_USSD_BOOL = "use_call_waiting_ussd_bool";
+
+ /**
* Specifies the service class for call waiting service.
* Default value is
* {@link com.android.internal.telephony.CommandsInterface#SERVICE_CLASS_VOICE}.
@@ -4820,6 +4828,7 @@
sDefaults.putBoolean(KEY_SUPPORT_EMERGENCY_DIALER_SHORTCUT_BOOL, true);
sDefaults.putBoolean(KEY_USE_CALL_FORWARDING_USSD_BOOL, false);
sDefaults.putBoolean(KEY_USE_CALLER_ID_USSD_BOOL, false);
+ sDefaults.putBoolean(KEY_USE_CALL_WAITING_USSD_BOOL, false);
sDefaults.putInt(KEY_CALL_WAITING_SERVICE_CLASS_INT, 1 /* SERVICE_CLASS_VOICE */);
sDefaults.putString(KEY_5G_ICON_CONFIGURATION_STRING,
"connected_mmwave:5G,connected:5G,not_restricted_rrc_idle:5G,"
diff --git a/telephony/java/android/telephony/ims/DelegateRegistrationState.java b/telephony/java/android/telephony/ims/DelegateRegistrationState.java
index fd206c1..c00c741 100644
--- a/telephony/java/android/telephony/ims/DelegateRegistrationState.java
+++ b/telephony/java/android/telephony/ims/DelegateRegistrationState.java
@@ -63,7 +63,7 @@
* This feature tag is being deregistered because the PDN that the IMS registration is on is
*changing.
* All open SIP dialogs need to be closed before the PDN change can proceed using
- * {@link SipDelegateConnection#closeDialog(String)}.
+ * {@link SipDelegateConnection#cleanupSession(String)}.
*/
public static final int DEREGISTERING_REASON_PDN_CHANGE = 3;
@@ -74,7 +74,7 @@
* a user triggered hange, such as data being enabled/disabled.
* <p>
* All open SIP dialogs associated with the new deprovisioned feature tag need to be closed
- * using {@link SipDelegateConnection#closeDialog(String)} before the IMS registration
+ * using {@link SipDelegateConnection#cleanupSession(String)} before the IMS registration
* modification can proceed.
*/
public static final int DEREGISTERING_REASON_PROVISIONING_CHANGE = 4;
@@ -84,7 +84,7 @@
* needs to change its supported feature set.
* <p>
* All open SIP Dialogs associated with this feature tag must be closed
- * using {@link SipDelegateConnection#closeDialog(String)} before this operation can proceed.
+ * using {@link SipDelegateConnection#cleanupSession(String)} before this operation can proceed.
*/
public static final int DEREGISTERING_REASON_FEATURE_TAGS_CHANGING = 5;
@@ -93,7 +93,7 @@
* destroyed.
* <p>
* All open SIP Dialogs associated with this feature tag must be closed
- * using {@link SipDelegateConnection#closeDialog(String)} before this operation can proceed.
+ * using {@link SipDelegateConnection#cleanupSession(String)} before this operation can proceed.
*/
public static final int DEREGISTERING_REASON_DESTROY_PENDING = 6;
diff --git a/telephony/java/android/telephony/ims/SipDelegateConnection.java b/telephony/java/android/telephony/ims/SipDelegateConnection.java
index 04a772c..d7a19bc 100644
--- a/telephony/java/android/telephony/ims/SipDelegateConnection.java
+++ b/telephony/java/android/telephony/ims/SipDelegateConnection.java
@@ -74,8 +74,30 @@
* closed.
* @param callId The call-ID header value associated with the ongoing SIP Dialog that is
* closing.
+ * @deprecated closeDialog does not capture INVITE forking. Use {@link #cleanupSession} instead.
*/
- void closeDialog(@NonNull String callId);
+ @Deprecated
+ default void closeDialog(@NonNull String callId) {
+ cleanupSession(callId);
+ }
+
+ /**
+ * The SIP session associated with the provided Call-ID is being closed and routing resources
+ * associated with the session are free to be released. Each SIP session may contain multiple
+ * dialogs due to SIP INVITE forking, so this method must be called after all SIP dialogs
+ * associated with the session has closed.
+ * <p>
+ * Calling this method is also mandatory for situations where the framework IMS stack is waiting
+ * for pending SIP sessions to be closed before it can perform a handover or apply a
+ * provisioning change. See {@link DelegateRegistrationState} for more information about
+ * the scenarios where this can occur.
+ * <p>
+ * This method will need to be called for each SIP session managed by this application when it
+ * is closed.
+ * @param callId The call-ID header value associated with the ongoing SIP Dialog that is
+ * closing.
+ */
+ default void cleanupSession(@NonNull String callId) { }
/**
* Notify the SIP delegate that the SIP message has been received from
diff --git a/telephony/java/android/telephony/ims/aidl/ISipDelegate.aidl b/telephony/java/android/telephony/ims/aidl/ISipDelegate.aidl
index ad75be4..ff1a8f0 100644
--- a/telephony/java/android/telephony/ims/aidl/ISipDelegate.aidl
+++ b/telephony/java/android/telephony/ims/aidl/ISipDelegate.aidl
@@ -26,5 +26,5 @@
void sendMessage(in SipMessage sipMessage, long configVersion);
void notifyMessageReceived(in String viaTransactionId);
void notifyMessageReceiveError(in String viaTransactionId, int reason);
- void closeDialog(in String callId);
+ void cleanupSession(in String callId);
}
diff --git a/telephony/java/android/telephony/ims/aidl/SipDelegateAidlWrapper.java b/telephony/java/android/telephony/ims/aidl/SipDelegateAidlWrapper.java
index 5c9ec53..6a98d80 100644
--- a/telephony/java/android/telephony/ims/aidl/SipDelegateAidlWrapper.java
+++ b/telephony/java/android/telephony/ims/aidl/SipDelegateAidlWrapper.java
@@ -79,11 +79,11 @@
}
@Override
- public void closeDialog(String callId) {
+ public void cleanupSession(String callId) {
SipDelegate d = mDelegate;
final long token = Binder.clearCallingIdentity();
try {
- mExecutor.execute(() -> d.closeDialog(callId));
+ mExecutor.execute(() -> d.cleanupSession(callId));
} finally {
Binder.restoreCallingIdentity(token);
}
diff --git a/telephony/java/android/telephony/ims/aidl/SipDelegateConnectionAidlWrapper.java b/telephony/java/android/telephony/ims/aidl/SipDelegateConnectionAidlWrapper.java
index ad02fe5..0abb495 100644
--- a/telephony/java/android/telephony/ims/aidl/SipDelegateConnectionAidlWrapper.java
+++ b/telephony/java/android/telephony/ims/aidl/SipDelegateConnectionAidlWrapper.java
@@ -200,13 +200,13 @@
}
@Override
- public void closeDialog(String callId) {
+ public void cleanupSession(String callId) {
try {
ISipDelegate conn = getSipDelegateBinder();
if (conn == null) {
return;
}
- conn.closeDialog(callId);
+ conn.cleanupSession(callId);
} catch (RemoteException e) {
// Nothing to do here, app will eventually get remote death callback.
}
diff --git a/telephony/java/android/telephony/ims/stub/SipDelegate.java b/telephony/java/android/telephony/ims/stub/SipDelegate.java
index b036b5e..d5198a0 100644
--- a/telephony/java/android/telephony/ims/stub/SipDelegate.java
+++ b/telephony/java/android/telephony/ims/stub/SipDelegate.java
@@ -76,8 +76,30 @@
*
* @param callId The call-ID header value associated with the ongoing SIP Dialog that the
* framework is requesting be closed.
+ * @deprecated This method does not take into account INVITE forking. Use
+ * {@link #cleanupSession(String)} instead.
*/
- void closeDialog(@NonNull String callId);
+ @Deprecated
+ default void closeDialog(@NonNull String callId) { }
+
+ /**
+ * The remote IMS application has closed a SIP session and the routing resources associated
+ * with the SIP session using the provided Call-ID may now be cleaned up.
+ * <p>
+ * Typically, a SIP session will be considered closed when all associated dialogs receive a
+ * BYE request. After the session has been closed, the IMS application will call
+ * {@link SipDelegateConnection#cleanupSession(String)} to signal to the framework that
+ * resources can be released. In some cases, the framework will request that the ImsService
+ * close the session due to the open SIP session holding up an event such as applying a
+ * provisioning change or handing over to another transport type. See
+ * {@link DelegateRegistrationState}.
+ *
+ * @param callId The call-ID header value associated with the ongoing SIP Session that the
+ * framework is requesting be cleaned up.
+ */
+ default void cleanupSession(@NonNull String callId) {
+ closeDialog(callId);
+ }
/**
* The remote application has received the SIP message and is processing it.
diff --git a/tests/net/common/java/android/net/TcpKeepalivePacketDataTest.kt b/tests/net/common/java/android/net/TcpKeepalivePacketDataTest.kt
index 6770066..7a18bb0 100644
--- a/tests/net/common/java/android/net/TcpKeepalivePacketDataTest.kt
+++ b/tests/net/common/java/android/net/TcpKeepalivePacketDataTest.kt
@@ -92,12 +92,12 @@
assertTrue(str.contains(data.dstAddress.hostAddress))
assertTrue(str.contains(data.dstPort.toString()))
// .packet not included in toString()
- assertTrue(str.contains(data.tcpSeq.toString()))
- assertTrue(str.contains(data.tcpAck.toString()))
- assertTrue(str.contains(data.tcpWindow.toString()))
- assertTrue(str.contains(data.tcpWindowScale.toString()))
- assertTrue(str.contains(data.ipTos.toString()))
- assertTrue(str.contains(data.ipTtl.toString()))
+ assertTrue(str.contains(data.getTcpSeq().toString()))
+ assertTrue(str.contains(data.getTcpAck().toString()))
+ assertTrue(str.contains(data.getTcpWindow().toString()))
+ assertTrue(str.contains(data.getTcpWindowScale().toString()))
+ assertTrue(str.contains(data.getIpTos().toString()))
+ assertTrue(str.contains(data.getIpTtl().toString()))
// Update above assertions if field is added
assertFieldCountEquals(5, KeepalivePacketData::class.java)
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index e050495..790f3be 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -18,6 +18,7 @@
import static android.Manifest.permission.CHANGE_NETWORK_STATE;
import static android.Manifest.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS;
+import static android.Manifest.permission.DUMP;
import static android.Manifest.permission.NETWORK_FACTORY;
import static android.Manifest.permission.NETWORK_SETTINGS;
import static android.app.PendingIntent.FLAG_IMMUTABLE;
@@ -356,6 +357,8 @@
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Predicate;
import java.util.function.Supplier;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
import java.util.stream.Collectors;
import kotlin.reflect.KClass;
@@ -9950,6 +9953,7 @@
@Test
public void testDumpDoesNotCrash() {
+ mServiceContext.setPermission(DUMP, PERMISSION_GRANTED);
// Filing a couple requests prior to testing the dump.
final TestNetworkCallback genericNetworkCallback = new TestNetworkCallback();
final TestNetworkCallback wifiNetworkCallback = new TestNetworkCallback();
@@ -11706,6 +11710,33 @@
}
@Test
+ public void testSetOemNetworkPreferenceLogsRequest() throws Exception {
+ mServiceContext.setPermission(DUMP, PERMISSION_GRANTED);
+ @OemNetworkPreferences.OemNetworkPreference final int networkPref =
+ OEM_NETWORK_PREFERENCE_OEM_PAID;
+ final StringWriter stringWriter = new StringWriter();
+ final String logIdentifier = "UPDATE INITIATED: OemNetworkPreferences";
+ final Pattern pattern = Pattern.compile(logIdentifier);
+
+ final int expectedNumLogs = 2;
+ final UidRangeParcel[] uidRanges =
+ toUidRangeStableParcels(uidRangesForUids(TEST_PACKAGE_UID));
+
+ // Call twice to generate two logs.
+ setupSetOemNetworkPreferenceForPreferenceTest(networkPref, uidRanges, TEST_PACKAGE_NAME);
+ setupSetOemNetworkPreferenceForPreferenceTest(networkPref, uidRanges, TEST_PACKAGE_NAME);
+ mService.dump(new FileDescriptor(), new PrintWriter(stringWriter), new String[0]);
+
+ final String dumpOutput = stringWriter.toString();
+ final Matcher matcher = pattern.matcher(dumpOutput);
+ int count = 0;
+ while (matcher.find()) {
+ count++;
+ }
+ assertEquals(expectedNumLogs, count);
+ }
+
+ @Test
public void testGetAllNetworkStateSnapshot() throws Exception {
verifyNoNetwork();
diff --git a/tests/vcn/java/com/android/server/VcnManagementServiceTest.java b/tests/vcn/java/com/android/server/VcnManagementServiceTest.java
index 43e6676..bbc9bb6 100644
--- a/tests/vcn/java/com/android/server/VcnManagementServiceTest.java
+++ b/tests/vcn/java/com/android/server/VcnManagementServiceTest.java
@@ -81,7 +81,6 @@
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
-import com.android.net.module.util.LocationPermissionChecker;
import com.android.server.VcnManagementService.VcnCallback;
import com.android.server.VcnManagementService.VcnStatusCallbackInfo;
import com.android.server.vcn.TelephonySubscriptionTracker;
@@ -162,8 +161,6 @@
mock(PersistableBundleUtils.LockingReadWriteHelper.class);
private final TelephonySubscriptionTracker mSubscriptionTracker =
mock(TelephonySubscriptionTracker.class);
- private final LocationPermissionChecker mLocationPermissionChecker =
- mock(LocationPermissionChecker.class);
private final ArgumentCaptor<VcnCallback> mVcnCallbackCaptor =
ArgumentCaptor.forClass(VcnCallback.class);
@@ -207,9 +204,6 @@
doReturn(mConfigReadWriteHelper)
.when(mMockDeps)
.newPersistableBundleLockingReadWriteHelper(any());
- doReturn(mLocationPermissionChecker)
- .when(mMockDeps)
- .newLocationPermissionChecker(eq(mMockContext));
// Setup VCN instance generation
doAnswer((invocation) -> {
@@ -521,10 +515,6 @@
@Test
public void testSetVcnConfigNotifiesStatusCallback() throws Exception {
- mVcnMgmtSvc.systemReady();
- doReturn(true)
- .when(mLocationPermissionChecker)
- .checkLocationPermission(eq(TEST_PACKAGE_NAME), any(), eq(TEST_UID), any());
triggerSubscriptionTrackerCbAndGetSnapshot(Collections.singleton(TEST_UUID_2));
mVcnMgmtSvc.registerVcnStatusCallback(TEST_UUID_2, mMockStatusCallback, TEST_PACKAGE_NAME);
@@ -697,10 +687,6 @@
doReturn(isVcnActive ? VCN_STATUS_CODE_ACTIVE : VCN_STATUS_CODE_SAFE_MODE)
.when(vcn)
.getStatus();
-
- doReturn(true)
- .when(mLocationPermissionChecker)
- .checkLocationPermission(eq(TEST_PACKAGE_NAME), any(), eq(TEST_UID), any());
}
private NetworkCapabilities.Builder getNetworkCapabilitiesBuilderForTransport(
@@ -933,8 +919,7 @@
@NonNull ParcelUuid subGroup,
@NonNull String pkgName,
int uid,
- boolean hasPermissionsforSubGroup,
- boolean hasLocationPermission)
+ boolean hasPermissionsforSubGroup)
throws Exception {
TelephonySubscriptionSnapshot snapshot =
triggerSubscriptionTrackerCbAndGetSnapshot(Collections.singleton(subGroup));
@@ -946,10 +931,6 @@
.when(snapshot)
.packageHasPermissionsForSubscriptionGroup(eq(subGroup), eq(pkgName));
- doReturn(hasLocationPermission)
- .when(mLocationPermissionChecker)
- .checkLocationPermission(eq(pkgName), any(), eq(uid), any());
-
mVcnMgmtSvc.registerVcnStatusCallback(subGroup, mMockStatusCallback, pkgName);
triggerVcnSafeMode(subGroup, snapshot, true /* enterSafeMode */);
@@ -959,11 +940,7 @@
public void testVcnStatusCallbackOnSafeModeStatusChangedWithCarrierPrivileges()
throws Exception {
triggerVcnStatusCallbackOnSafeModeStatusChanged(
- TEST_UUID_1,
- TEST_PACKAGE_NAME,
- TEST_UID,
- true /* hasPermissionsforSubGroup */,
- true /* hasLocationPermission */);
+ TEST_UUID_1, TEST_PACKAGE_NAME, TEST_UID, true /* hasPermissionsforSubGroup */);
verify(mMockStatusCallback).onVcnStatusChanged(VcnManager.VCN_STATUS_CODE_SAFE_MODE);
}
@@ -972,25 +949,7 @@
public void testVcnStatusCallbackOnSafeModeStatusChangedWithoutCarrierPrivileges()
throws Exception {
triggerVcnStatusCallbackOnSafeModeStatusChanged(
- TEST_UUID_1,
- TEST_PACKAGE_NAME,
- TEST_UID,
- false /* hasPermissionsforSubGroup */,
- true /* hasLocationPermission */);
-
- verify(mMockStatusCallback, never())
- .onVcnStatusChanged(VcnManager.VCN_STATUS_CODE_SAFE_MODE);
- }
-
- @Test
- public void testVcnStatusCallbackOnSafeModeStatusChangedWithoutLocationPermission()
- throws Exception {
- triggerVcnStatusCallbackOnSafeModeStatusChanged(
- TEST_UUID_1,
- TEST_PACKAGE_NAME,
- TEST_UID,
- true /* hasPermissionsforSubGroup */,
- false /* hasLocationPermission */);
+ TEST_UUID_1, TEST_PACKAGE_NAME, TEST_UID, false /* hasPermissionsforSubGroup */);
verify(mMockStatusCallback, never())
.onVcnStatusChanged(VcnManager.VCN_STATUS_CODE_SAFE_MODE);
@@ -1052,9 +1011,6 @@
.when(snapshot)
.packageHasPermissionsForSubscriptionGroup(
eq(TEST_UUID_1), eq(TEST_CB_PACKAGE_NAME));
- doReturn(true)
- .when(mLocationPermissionChecker)
- .checkLocationPermission(eq(TEST_CB_PACKAGE_NAME), any(), eq(TEST_UID), any());
mVcnMgmtSvc.registerVcnStatusCallback(
TEST_UUID_1, mMockStatusCallback, TEST_CB_PACKAGE_NAME);
diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java
index 34c0018..bb67593 100644
--- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java
+++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java
@@ -26,6 +26,7 @@
import static com.android.server.vcn.VcnGatewayConnection.VcnChildSessionConfiguration;
import static com.android.server.vcn.VcnGatewayConnection.VcnIkeSession;
+import static com.android.server.vcn.VcnGatewayConnection.VcnNetworkAgent;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -46,15 +47,19 @@
import android.net.LinkProperties;
import android.net.NetworkAgent;
import android.net.NetworkCapabilities;
+import android.net.ipsec.ike.ChildSaProposal;
import android.net.ipsec.ike.exceptions.AuthenticationFailedException;
import android.net.ipsec.ike.exceptions.IkeException;
import android.net.ipsec.ike.exceptions.IkeInternalException;
import android.net.ipsec.ike.exceptions.TemporaryFailureException;
+import android.net.vcn.VcnControlPlaneIkeConfig;
import android.net.vcn.VcnManager.VcnErrorCode;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
+import com.android.server.vcn.util.MtuUtils;
+
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -73,13 +78,13 @@
@SmallTest
public class VcnGatewayConnectionConnectedStateTest extends VcnGatewayConnectionTestBase {
private VcnIkeSession mIkeSession;
- private NetworkAgent mNetworkAgent;
+ private VcnNetworkAgent mNetworkAgent;
@Before
public void setUp() throws Exception {
super.setUp();
- mNetworkAgent = mock(NetworkAgent.class);
+ mNetworkAgent = mock(VcnNetworkAgent.class);
doReturn(mNetworkAgent)
.when(mDeps)
.newNetworkAgent(any(), any(), any(), any(), anyInt(), any(), any(), any(), any());
@@ -152,7 +157,9 @@
}
@Test
- public void testMigratedTransformsAreApplied() throws Exception {
+ public void testMigration() throws Exception {
+ triggerChildOpened();
+
getChildSessionCallback()
.onIpSecTransformsMigrated(makeDummyIpSecTransform(), makeDummyIpSecTransform());
mTestLooper.dispatchAll();
@@ -170,6 +177,17 @@
}
assertEquals(mGatewayConnection.mConnectedState, mGatewayConnection.getCurrentState());
+
+ final List<ChildSaProposal> saProposals =
+ ((VcnControlPlaneIkeConfig) mConfig.getControlPlaneConfig())
+ .getChildSessionParams()
+ .getSaProposals();
+ final int expectedMtu =
+ MtuUtils.getMtu(
+ saProposals,
+ mConfig.getMaxMtu(),
+ TEST_UNDERLYING_NETWORK_RECORD_1.linkProperties.getMtu());
+ verify(mNetworkAgent).sendLinkProperties(argThat(lp -> expectedMtu == lp.getMtu()));
}
private void triggerChildOpened() {
@@ -299,8 +317,9 @@
.removeAddressFromTunnelInterface(
eq(TEST_IPSEC_TUNNEL_RESOURCE_ID), eq(TEST_INTERNAL_ADDR), any());
- // TODO(b/184579891): Also verify link properties updated and sent when sendLinkProperties
- // is mockable
+ verify(mNetworkAgent).sendLinkProperties(argThat(
+ lp -> newInternalAddrs.equals(lp.getLinkAddresses())
+ && Collections.singletonList(TEST_DNS_ADDR_2).equals(lp.getDnsServers())));
// Verify that IpSecTunnelInterface only created once
verify(mIpSecSvc).createTunnelInterface(any(), any(), any(), any(), any());
@@ -323,6 +342,66 @@
assertFalse(mGatewayConnection.isInSafeMode());
}
+ private Consumer<VcnNetworkAgent> setupNetworkAndGetUnwantedCallback() {
+ triggerChildOpened();
+ mTestLooper.dispatchAll();
+
+ final ArgumentCaptor<Consumer<VcnNetworkAgent>> unwantedCallbackCaptor =
+ ArgumentCaptor.forClass(Consumer.class);
+ verify(mDeps)
+ .newNetworkAgent(
+ any(),
+ any(),
+ any(),
+ any(),
+ anyInt(),
+ any(),
+ any(),
+ unwantedCallbackCaptor.capture(),
+ any());
+
+ return unwantedCallbackCaptor.getValue();
+ }
+
+ @Test
+ public void testUnwantedNetworkAgentTriggersTeardown() throws Exception {
+ final Consumer<VcnNetworkAgent> unwantedCallback = setupNetworkAndGetUnwantedCallback();
+
+ unwantedCallback.accept(mNetworkAgent);
+ mTestLooper.dispatchAll();
+
+ assertTrue(mGatewayConnection.isQuitting());
+ assertEquals(mGatewayConnection.mDisconnectingState, mGatewayConnection.getCurrentState());
+ }
+
+ @Test
+ public void testUnwantedNetworkAgentWithDisconnectedNetworkAgent() throws Exception {
+ final Consumer<VcnNetworkAgent> unwantedCallback = setupNetworkAndGetUnwantedCallback();
+
+ mGatewayConnection.setNetworkAgent(null);
+ unwantedCallback.accept(mNetworkAgent);
+ mTestLooper.dispatchAll();
+
+ // Verify that the call was ignored; the state machine is still running, and the state has
+ // not changed.
+ assertFalse(mGatewayConnection.isQuitting());
+ assertEquals(mGatewayConnection.mConnectedState, mGatewayConnection.getCurrentState());
+ }
+
+ @Test
+ public void testUnwantedNetworkAgentWithNewNetworkAgent() throws Exception {
+ final Consumer<VcnNetworkAgent> unwantedCallback = setupNetworkAndGetUnwantedCallback();
+ final VcnNetworkAgent testAgent = mock(VcnNetworkAgent.class);
+
+ mGatewayConnection.setNetworkAgent(testAgent);
+ unwantedCallback.accept(mNetworkAgent);
+ mTestLooper.dispatchAll();
+
+ assertFalse(mGatewayConnection.isQuitting());
+ assertEquals(mGatewayConnection.mConnectedState, mGatewayConnection.getCurrentState());
+ assertEquals(testAgent, mGatewayConnection.getNetworkAgent());
+ }
+
@Test
public void testChildSessionClosedTriggersDisconnect() throws Exception {
// Verify scheduled but not canceled when entering ConnectedState
diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java
index c5ed8f6..dc73be2 100644
--- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java
+++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java
@@ -18,6 +18,7 @@
import static com.android.server.vcn.UnderlyingNetworkTracker.UnderlyingNetworkRecord;
import static com.android.server.vcn.VcnGatewayConnection.VcnIkeSession;
+import static com.android.server.vcn.VcnGatewayConnection.VcnNetworkAgent;
import static com.android.server.vcn.VcnTestUtils.setupIpSecManager;
import static org.junit.Assert.assertEquals;
@@ -44,7 +45,6 @@
import android.net.LinkAddress;
import android.net.LinkProperties;
import android.net.Network;
-import android.net.NetworkAgent;
import android.net.NetworkCapabilities;
import android.net.ipsec.ike.ChildSessionCallback;
import android.net.ipsec.ike.IkeSessionCallback;
@@ -90,12 +90,18 @@
protected static final int TEST_SUB_ID = 5;
protected static final long ELAPSED_REAL_TIME = 123456789L;
protected static final String TEST_IPSEC_TUNNEL_IFACE = "IPSEC_IFACE";
+
protected static final UnderlyingNetworkRecord TEST_UNDERLYING_NETWORK_RECORD_1 =
new UnderlyingNetworkRecord(
new Network(0),
new NetworkCapabilities(),
new LinkProperties(),
false /* blocked */);
+
+ static {
+ TEST_UNDERLYING_NETWORK_RECORD_1.linkProperties.setMtu(1500);
+ }
+
protected static final UnderlyingNetworkRecord TEST_UNDERLYING_NETWORK_RECORD_2 =
new UnderlyingNetworkRecord(
new Network(1),
@@ -103,6 +109,10 @@
new LinkProperties(),
false /* blocked */);
+ static {
+ TEST_UNDERLYING_NETWORK_RECORD_2.linkProperties.setMtu(1460);
+ }
+
protected static final TelephonySubscriptionSnapshot TEST_SUBSCRIPTION_SNAPSHOT =
new TelephonySubscriptionSnapshot(
Collections.singletonMap(TEST_SUB_ID, TEST_SUB_GRP), Collections.EMPTY_MAP);
@@ -278,8 +288,8 @@
protected void verifySafeModeTimeoutNotifiesCallbackAndUnregistersNetworkAgent(
@NonNull State expectedState) {
- // Set a NetworkAgent, and expect it to be unregistered and cleared
- final NetworkAgent mockNetworkAgent = mock(NetworkAgent.class);
+ // Set a VcnNetworkAgent, and expect it to be unregistered and cleared
+ final VcnNetworkAgent mockNetworkAgent = mock(VcnNetworkAgent.class);
mGatewayConnection.setNetworkAgent(mockNetworkAgent);
// SafeMode timer starts when VcnGatewayConnection exits DisconnectedState (the initial
diff --git a/tests/vcn/java/com/android/server/vcn/util/MtuUtilsTest.java b/tests/vcn/java/com/android/server/vcn/util/MtuUtilsTest.java
new file mode 100644
index 0000000..29511f7
--- /dev/null
+++ b/tests/vcn/java/com/android/server/vcn/util/MtuUtilsTest.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.vcn.util;
+
+import static android.net.ipsec.ike.SaProposal.ENCRYPTION_ALGORITHM_AES_CBC;
+import static android.net.ipsec.ike.SaProposal.ENCRYPTION_ALGORITHM_AES_GCM_12;
+import static android.net.ipsec.ike.SaProposal.ENCRYPTION_ALGORITHM_AES_GCM_16;
+import static android.net.ipsec.ike.SaProposal.ENCRYPTION_ALGORITHM_AES_GCM_8;
+import static android.net.ipsec.ike.SaProposal.INTEGRITY_ALGORITHM_HMAC_SHA2_256_128;
+import static android.net.ipsec.ike.SaProposal.KEY_LEN_AES_256;
+
+import static com.android.net.module.util.NetworkStackConstants.ETHER_MTU;
+import static com.android.net.module.util.NetworkStackConstants.IPV6_MIN_MTU;
+import static com.android.server.vcn.util.MtuUtils.getMtu;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import static java.util.Collections.emptyList;
+
+import android.net.ipsec.ike.ChildSaProposal;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Arrays;
+import java.util.List;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class MtuUtilsTest {
+ @Test
+ public void testUnderlyingMtuZero() {
+ assertEquals(
+ IPV6_MIN_MTU, getMtu(emptyList(), ETHER_MTU /* maxMtu */, 0 /* underlyingMtu */));
+ }
+
+ @Test
+ public void testClampsToMaxMtu() {
+ assertEquals(0, getMtu(emptyList(), 0 /* maxMtu */, IPV6_MIN_MTU /* underlyingMtu */));
+ }
+
+ @Test
+ public void testNormalModeAlgorithmLessThanUnderlyingMtu() {
+ final List<ChildSaProposal> saProposals =
+ Arrays.asList(
+ new ChildSaProposal.Builder()
+ .addEncryptionAlgorithm(
+ ENCRYPTION_ALGORITHM_AES_CBC, KEY_LEN_AES_256)
+ .addIntegrityAlgorithm(INTEGRITY_ALGORITHM_HMAC_SHA2_256_128)
+ .build());
+
+ final int actualMtu =
+ getMtu(saProposals, ETHER_MTU /* maxMtu */, ETHER_MTU /* underlyingMtu */);
+ assertTrue(ETHER_MTU > actualMtu);
+ }
+
+ @Test
+ public void testCombinedModeAlgorithmLessThanUnderlyingMtu() {
+ final List<ChildSaProposal> saProposals =
+ Arrays.asList(
+ new ChildSaProposal.Builder()
+ .addEncryptionAlgorithm(
+ ENCRYPTION_ALGORITHM_AES_GCM_16, KEY_LEN_AES_256)
+ .addEncryptionAlgorithm(
+ ENCRYPTION_ALGORITHM_AES_GCM_12, KEY_LEN_AES_256)
+ .addEncryptionAlgorithm(
+ ENCRYPTION_ALGORITHM_AES_GCM_8, KEY_LEN_AES_256)
+ .build());
+
+ final int actualMtu =
+ getMtu(saProposals, ETHER_MTU /* maxMtu */, ETHER_MTU /* underlyingMtu */);
+ assertTrue(ETHER_MTU > actualMtu);
+ }
+}
diff --git a/tools/hiddenapi/checksorted_sha.sh b/tools/hiddenapi/checksorted_sha.sh
index ceb705f..451fed6 100755
--- a/tools/hiddenapi/checksorted_sha.sh
+++ b/tools/hiddenapi/checksorted_sha.sh
@@ -1,7 +1,7 @@
#!/bin/bash
set -e
LOCAL_DIR="$( dirname ${BASH_SOURCE} )"
-git show --name-only --pretty=format: $1 | grep "config/hiddenapi-.*txt" | while read file; do
+git show --name-only --pretty=format: $1 | grep "boot/hiddenapi/hiddenapi-.*txt" | while read file; do
diff <(git show $1:$file) <(git show $1:$file | $LOCAL_DIR/sort_api.sh ) || {
echo -e "\e[1m\e[31m$file $1 is not sorted or contains duplicates. To sort it correctly:\e[0m"
echo -e "\e[33m${LOCAL_DIR}/sort_api.sh $2/frameworks/base/$file\e[0m"
diff --git a/tools/hiddenapi/exclude.sh b/tools/hiddenapi/exclude.sh
index 2924e01..822aba4 100755
--- a/tools/hiddenapi/exclude.sh
+++ b/tools/hiddenapi/exclude.sh
@@ -48,7 +48,7 @@
PACKAGES=$(for t in $TEAMS; do echo $(eval echo \${${t}_PACKAGES}); done)
RE=$(echo ${PACKAGES} | sed "s/ /|/g")
EXIT_CODE=0
-for file in $(git show --name-only --pretty=format: $SHA | grep "config/hiddenapi-.*txt"); do
+for file in $(git show --name-only --pretty=format: $SHA | grep "boot/hiddenapi/hiddenapi-.*txt"); do
ENTRIES=$(grep -E "^\+L(${RE})/" <(git diff ${SHA}~1 ${SHA} $file) | sed "s|^\+||" || echo)
if [[ -n "${ENTRIES}" ]]; then
echo -e "\e[1m\e[31m$file $SHA contains the following entries\e[0m"