Merge changes from topic "truncate operator name" am: ffa0f982b6 am: 9cf73e369d am: eb897b1d85

Original change: https://android-review.googlesource.com/c/platform/frameworks/base/+/2004810

Change-Id: Ie1a67a1a9fae4dbbc321755f7ec9bde8bb84613f
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
Ling Ma 2022-04-01 22:54:46 +00:00 committed by Automerger Merge Worker
commit 8827b92855
3 changed files with 142 additions and 1 deletions

View File

@ -350,6 +350,53 @@ public class TextUtils {
return ret; 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. * Returns a string containing the tokens joined by delimiters.
* *

View File

@ -43,6 +43,7 @@ import com.google.android.collect.Lists;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Locale; import java.util.Locale;
@ -781,6 +782,81 @@ public class TextUtilsTest {
TextUtils.trimToSize("abc", 0); TextUtils.trimToSize("abc", 0);
} }
@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 @Test
public void length() { public void length() {
assertEquals(0, TextUtils.length(null)); assertEquals(0, TextUtils.length(null));

View File

@ -11768,7 +11768,25 @@ public class TelephonyManager {
if (SubscriptionManager.isValidPhoneId(phoneId)) { if (SubscriptionManager.isValidPhoneId(phoneId)) {
List<String> newList = updateTelephonyProperty( List<String> newList = updateTelephonyProperty(
TelephonyProperties.operator_alpha(), phoneId, name); 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);
}
} }
} }