diff options
7 files changed, 166 insertions, 4 deletions
diff --git a/core/java/android/os/SystemProperties.java b/core/java/android/os/SystemProperties.java index 82d4443ea724..aa283a2d019b 100644 --- a/core/java/android/os/SystemProperties.java +++ b/core/java/android/os/SystemProperties.java @@ -226,9 +226,10 @@ public class SystemProperties { */ @UnsupportedAppUsage public static void set(@NonNull String key, @Nullable String val) { - if (val != null && !key.startsWith("ro.") && val.length() > PROP_VALUE_MAX) { + if (val != null && !key.startsWith("ro.") && val.getBytes(StandardCharsets.UTF_8).length + > PROP_VALUE_MAX) { throw new IllegalArgumentException("value of system property '" + key - + "' is longer than " + PROP_VALUE_MAX + " characters: " + val); + + "' is longer than " + PROP_VALUE_MAX + " bytes: " + val); } if (TRACK_KEY_ACCESS) onKeyAccess(key); native_set(key, val); diff --git a/core/java/android/text/TextUtils.java b/core/java/android/text/TextUtils.java index d0fd2b393633..509f4d996ae9 100644 --- a/core/java/android/text/TextUtils.java +++ b/core/java/android/text/TextUtils.java @@ -350,6 +350,53 @@ public class TextUtils { return ret; } + + /** + * Returns the longest prefix of a string for which the UTF-8 encoding fits into the given + * number of bytes, with the additional guarantee that the string is not truncated in the middle + * of a valid surrogate pair. + * + * <p>Unpaired surrogates are counted as taking 3 bytes of storage. However, a subsequent + * attempt to actually encode a string containing unpaired surrogates is likely to be rejected + * by the UTF-8 implementation. + * + * (copied from google/thirdparty) + * + * @param str a string + * @param maxbytes the maximum number of UTF-8 encoded bytes + * @return the beginning of the string, so that it uses at most maxbytes bytes in UTF-8 + * @throws IndexOutOfBoundsException if maxbytes is negative + * + * @hide + */ + public static String truncateStringForUtf8Storage(String str, int maxbytes) { + if (maxbytes < 0) { + throw new IndexOutOfBoundsException(); + } + + int bytes = 0; + for (int i = 0, len = str.length(); i < len; i++) { + char c = str.charAt(i); + if (c < 0x80) { + bytes += 1; + } else if (c < 0x800) { + bytes += 2; + } else if (c < Character.MIN_SURROGATE + || c > Character.MAX_SURROGATE + || str.codePointAt(i) < Character.MIN_SUPPLEMENTARY_CODE_POINT) { + bytes += 3; + } else { + bytes += 4; + i += (bytes > maxbytes) ? 0 : 1; + } + if (bytes > maxbytes) { + return str.substring(0, i); + } + } + return str; + } + + /** * Returns a string containing the tokens joined by delimiters. * diff --git a/core/tests/coretests/src/android/text/TextUtilsTest.java b/core/tests/coretests/src/android/text/TextUtilsTest.java index a0fc34923a4d..c4bcfd4cf117 100644 --- a/core/tests/coretests/src/android/text/TextUtilsTest.java +++ b/core/tests/coretests/src/android/text/TextUtilsTest.java @@ -43,6 +43,7 @@ import com.google.android.collect.Lists; import org.junit.Test; import org.junit.runner.RunWith; +import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.List; import java.util.Locale; @@ -782,6 +783,81 @@ public class TextUtilsTest { } @Test + public void truncateStringForUtf8Storage() { + assertEquals("", TextUtils.truncateStringForUtf8Storage("abc", 0)); + + //================ long normal case ================ + StringBuilder builder = new StringBuilder(); + + int n = 50; + for (int i = 0; i < 2 * n; i++) { + builder.append("哈"); + } + String initial = builder.toString(); + String result = TextUtils.truncateStringForUtf8Storage(initial, n); + + // Result should be the beginning of initial + assertTrue(initial.startsWith(result)); + + // Result should take less than n bytes in UTF-8 + assertTrue(result.getBytes(StandardCharsets.UTF_8).length <= n); + + // result + the next codePoint should take strictly more than + // n bytes in UTF-8 + assertTrue(initial.substring(0, initial.offsetByCodePoints(result.length(), 1)) + .getBytes(StandardCharsets.UTF_8).length > n); + + // =================== short normal case ===================== + String s = "sf\u20ACgk\u00E9ls\u00E9fg"; + result = TextUtils.truncateStringForUtf8Storage(s, 100); + assertEquals(s, result); + } + + @Test + public void testTruncateInMiddleOfSurrogate() { + StringBuilder builder = new StringBuilder(); + String beginning = "a"; + builder.append(beginning); + builder.append(Character.toChars(0x1D11E)); + + String result = TextUtils.truncateStringForUtf8Storage(builder.toString(), 3); + + // \u1D11E is a surrogate and needs 4 bytes in UTF-8. beginning == "a" uses + // only 1 bytes in UTF8 + // As we allow only 3 bytes for the whole string, so just 2 for this + // codePoint, there is not enough place and the string will be truncated + // just before it + assertEquals(beginning, result); + } + + @Test + public void testTruncateInMiddleOfChar() { + StringBuilder builder = new StringBuilder(); + String beginning = "a"; + builder.append(beginning); + builder.append(Character.toChars(0x20AC)); + + String result = TextUtils.truncateStringForUtf8Storage(builder.toString(), 3); + + // Like above, \u20AC uses 3 bytes in UTF-8, with "beginning", that makes + // 4 bytes so it is too big and should be truncated + assertEquals(beginning, result); + } + + @Test + public void testTruncateSubString() { + String test = "sdgkl;hjsl;gjhdgkljdfhglkdj"; + String sub = test.substring(10, 20); + String res = TextUtils.truncateStringForUtf8Storage(sub, 255); + assertEquals(sub, res); + } + + @Test(expected = IndexOutOfBoundsException.class) + public void truncateStringForUtf8StorageThrowsExceptionForNegativeSize() { + TextUtils.truncateStringForUtf8Storage("abc", -1); + } + + @Test public void length() { assertEquals(0, TextUtils.length(null)); assertEquals(0, TextUtils.length("")); diff --git a/services/core/java/com/android/server/vcn/VcnGatewayConnection.java b/services/core/java/com/android/server/vcn/VcnGatewayConnection.java index 597f7f284730..be38005abb63 100644 --- a/services/core/java/com/android/server/vcn/VcnGatewayConnection.java +++ b/services/core/java/com/android/server/vcn/VcnGatewayConnection.java @@ -544,6 +544,7 @@ public class VcnGatewayConnection extends StateMachine { private final boolean mIsMobileDataEnabled; @NonNull private final IpSecManager mIpSecManager; + @NonNull private final ConnectivityManager mConnectivityManager; @Nullable private IpSecTunnelInterface mTunnelIface = null; @@ -701,6 +702,7 @@ public class VcnGatewayConnection extends StateMachine { mLastSnapshot, mUnderlyingNetworkControllerCallback); mIpSecManager = mVcnContext.getContext().getSystemService(IpSecManager.class); + mConnectivityManager = mVcnContext.getContext().getSystemService(ConnectivityManager.class); addState(mDisconnectedState); addState(mDisconnectingState); @@ -1683,6 +1685,14 @@ public class VcnGatewayConnection extends StateMachine { clearFailedAttemptCounterAndSafeModeAlarm(); break; case NetworkAgent.VALIDATION_STATUS_NOT_VALID: + // Trigger re-validation of underlying networks; if it + // fails, the VCN will attempt to migrate away. + if (mUnderlying != null) { + mConnectivityManager.reportNetworkConnectivity( + mUnderlying.network, + false /* hasConnectivity */); + } + // Will only set a new alarm if no safe mode alarm is // currently scheduled. setSafeModeAlarm(); @@ -1869,6 +1879,10 @@ public class VcnGatewayConnection extends StateMachine { IpSecManager.DIRECTION_OUT); updateNetworkAgent(mTunnelIface, mNetworkAgent, mChildConfig); + + // Trigger re-validation after migration events. + mConnectivityManager.reportNetworkConnectivity( + mNetworkAgent.getNetwork(), false /* hasConnectivity */); } private void handleUnderlyingNetworkChanged(@NonNull Message msg) { diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java index a5adf52051b9..b4825153bf0a 100644 --- a/telephony/java/android/telephony/TelephonyManager.java +++ b/telephony/java/android/telephony/TelephonyManager.java @@ -11487,7 +11487,25 @@ public class TelephonyManager { if (SubscriptionManager.isValidPhoneId(phoneId)) { List<String> newList = updateTelephonyProperty( TelephonyProperties.operator_alpha(), phoneId, name); - TelephonyProperties.operator_alpha(newList); + try { + TelephonyProperties.operator_alpha(newList); + } catch (IllegalArgumentException e) { //property value is longer than the byte limit + Log.e(TAG, "setNetworkOperatorNameForPhone: ", e); + + int numberOfEntries = newList.size(); + int maxOperatorLength = //save 1 byte for joiner " , " + (SystemProperties.PROP_VALUE_MAX - numberOfEntries) / numberOfEntries; + + //examine and truncate every operator and retry + for (int i = 0; i < newList.size(); i++) { + if (newList.get(i) != null) { + newList.set(i, TextUtils + .truncateStringForUtf8Storage(newList.get(i), maxOperatorLength)); + } + } + TelephonyProperties.operator_alpha(newList); + Log.e(TAG, "successfully truncated operator_alpha: " + newList); + } } } diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java index 4cfa93b4ecf9..841b81c2a282 100644 --- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java +++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java @@ -252,6 +252,9 @@ public class VcnGatewayConnectionConnectedStateTest extends VcnGatewayConnection && TEST_TCP_BUFFER_SIZES_2.equals(lp.getTcpBufferSizes()))); verify(mNetworkAgent) .setUnderlyingNetworks(eq(singletonList(TEST_UNDERLYING_NETWORK_RECORD_2.network))); + + // Verify revalidation is triggered on VCN network + verify(mConnMgr).reportNetworkConnectivity(eq(mNetworkAgent.getNetwork()), eq(false)); } private void triggerChildOpened() { @@ -425,6 +428,9 @@ public class VcnGatewayConnectionConnectedStateTest extends VcnGatewayConnection triggerValidation(NetworkAgent.VALIDATION_STATUS_NOT_VALID); mTestLooper.dispatchAll(); + verify(mConnMgr) + .reportNetworkConnectivity(eq(TEST_UNDERLYING_NETWORK_RECORD_1.network), eq(false)); + final ArgumentCaptor<Runnable> runnableCaptor = ArgumentCaptor.forClass(Runnable.class); verify(mDeps, times(2)) .newWakeupMessage( diff --git a/tools/aapt2/util/Files.cpp b/tools/aapt2/util/Files.cpp index be09545abb45..383be56bb227 100644 --- a/tools/aapt2/util/Files.cpp +++ b/tools/aapt2/util/Files.cpp @@ -349,7 +349,7 @@ Maybe<std::vector<std::string>> FindFiles(const android::StringPiece& path, IDia const std::string root_dir = path.to_string(); std::unique_ptr<DIR, decltype(closedir)*> d(opendir(root_dir.data()), closedir); if (!d) { - diag->Error(DiagMessage() << SystemErrorCodeToString(errno)); + diag->Error(DiagMessage() << SystemErrorCodeToString(errno) << ": " << root_dir); return {}; } |