Loading apex/jobscheduler/service/java/com/android/server/job/JobStore.java +110 −23 Original line number Diff line number Diff line Loading @@ -16,6 +16,9 @@ package com.android.server.job; import static android.net.NetworkCapabilities.NET_CAPABILITY_TEMPORARILY_NOT_METERED; import static android.net.NetworkCapabilities.TRANSPORT_TEST; import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock; import static com.android.server.job.JobSchedulerService.sSystemClock; Loading @@ -30,6 +33,7 @@ import android.os.PersistableBundle; import android.os.Process; import android.os.SystemClock; import android.os.UserHandle; import android.text.TextUtils; import android.text.format.DateUtils; import android.util.ArraySet; import android.util.AtomicFile; Loading Loading @@ -62,6 +66,7 @@ import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.List; import java.util.Set; import java.util.StringJoiner; import java.util.function.Consumer; import java.util.function.Predicate; Loading Loading @@ -383,6 +388,36 @@ public final class JobStore { return true; } /** * Returns a single string representation of the contents of the specified intArray. * If the intArray is [1, 2, 4] as the input, the return result will be the string "1,2,4". */ @VisibleForTesting static String intArrayToString(int[] values) { final StringJoiner sj = new StringJoiner(","); for (final int value : values) { sj.add(String.valueOf(value)); } return sj.toString(); } /** * Converts a string containing a comma-separated list of decimal representations * of ints into an array of int. If the string is not correctly formatted, * or if any value doesn't fit into an int, NumberFormatException is thrown. */ @VisibleForTesting static int[] stringToIntArray(String str) { if (TextUtils.isEmpty(str)) return new int[0]; final String[] arr = str.split(","); final int[] values = new int[arr.length]; for (int i = 0; i < arr.length; i++) { values[i] = Integer.parseInt(arr[i]); } return values; } /** * Runnable that writes {@link #mJobSet} out to xml. * NOTE: This Runnable locks on mLock Loading Loading @@ -540,15 +575,12 @@ public final class JobStore { out.startTag(null, XML_TAG_PARAMS_CONSTRAINTS); if (jobStatus.hasConnectivityConstraint()) { final NetworkRequest network = jobStatus.getJob().getRequiredNetwork(); // STOPSHIP b/183071974: improve the scheme for backward compatibility and // mainline cleanliness. out.attribute(null, "net-capabilities", Long.toString( BitUtils.packBits(network.getCapabilities()))); out.attribute(null, "net-unwanted-capabilities", Long.toString( BitUtils.packBits(network.getForbiddenCapabilities()))); out.attribute(null, "net-transport-types", Long.toString( BitUtils.packBits(network.getTransportTypes()))); out.attribute(null, "net-capabilities-csv", intArrayToString( network.getCapabilities())); out.attribute(null, "net-forbidden-capabilities-csv", intArrayToString( network.getForbiddenCapabilities())); out.attribute(null, "net-transport-types-csv", intArrayToString( network.getTransportTypes())); } if (jobStatus.hasIdleConstraint()) { out.attribute(null, "idle", Boolean.toString(true)); Loading Loading @@ -822,7 +854,14 @@ public final class JobStore { } catch (NumberFormatException e) { Slog.d(TAG, "Error reading constraints, skipping."); return null; } catch (XmlPullParserException e) { Slog.d(TAG, "Error Parser Exception.", e); return null; } catch (IOException e) { Slog.d(TAG, "Error I/O Exception.", e); return null; } parser.next(); // Consume </constraints> // Read out execution parameters tag. Loading Loading @@ -964,30 +1003,78 @@ public final class JobStore { return new JobInfo.Builder(jobId, cname); } private void buildConstraintsFromXml(JobInfo.Builder jobBuilder, XmlPullParser parser) { /** * In S, there has been a change in format to make the code more robust and more * maintainable. * If the capabities are bits 4, 14, 15, the format in R, it is a long string as * netCapabilitiesLong = '49168' from the old XML file attribute "net-capabilities". * The format in S is the int array string as netCapabilitiesIntArray = '4,14,15' * from the new XML file attribute "net-capabilities-array". * For backward compatibility, when reading old XML the old format is still supported in * reading, but in order to avoid issues with OEM-defined flags, the accepted capabilities * are limited to that(maxNetCapabilityInR & maxTransportInR) defined in R. */ private void buildConstraintsFromXml(JobInfo.Builder jobBuilder, XmlPullParser parser) throws XmlPullParserException, IOException { String val; final String netCapabilities = parser.getAttributeValue(null, "net-capabilities"); final String netforbiddenCapabilities = parser.getAttributeValue( String netCapabilitiesLong = null; String netForbiddenCapabilitiesLong = null; String netTransportTypesLong = null; final String netCapabilitiesIntArray = parser.getAttributeValue( null, "net-capabilities-csv"); final String netForbiddenCapabilitiesIntArray = parser.getAttributeValue( null, "net-forbidden-capabilities-csv"); final String netTransportTypesIntArray = parser.getAttributeValue( null, "net-transport-types-csv"); if (netCapabilitiesIntArray == null || netTransportTypesIntArray == null) { netCapabilitiesLong = parser.getAttributeValue(null, "net-capabilities"); netForbiddenCapabilitiesLong = parser.getAttributeValue( null, "net-unwanted-capabilities"); final String netTransportTypes = parser.getAttributeValue(null, "net-transport-types"); if (netCapabilities != null && netTransportTypes != null) { netTransportTypesLong = parser.getAttributeValue(null, "net-transport-types"); } if ((netCapabilitiesIntArray != null) && (netTransportTypesIntArray != null)) { final NetworkRequest.Builder builder = new NetworkRequest.Builder() .clearCapabilities(); final long forbiddenCapabilities = netforbiddenCapabilities != null ? Long.parseLong(netforbiddenCapabilities) : BitUtils.packBits(builder.build().getForbiddenCapabilities()); for (int capability : stringToIntArray(netCapabilitiesIntArray)) { builder.addCapability(capability); } for (int forbiddenCapability : stringToIntArray(netForbiddenCapabilitiesIntArray)) { builder.addForbiddenCapability(forbiddenCapability); } for (int transport : stringToIntArray(netTransportTypesIntArray)) { builder.addTransportType(transport); } jobBuilder.setRequiredNetwork(builder.build()); } else if (netCapabilitiesLong != null && netTransportTypesLong != null) { final NetworkRequest.Builder builder = new NetworkRequest.Builder() .clearCapabilities(); final int maxNetCapabilityInR = NET_CAPABILITY_TEMPORARILY_NOT_METERED; // We're okay throwing NFE here; caught by caller for (int capability : BitUtils.unpackBits(Long.parseLong(netCapabilities))) { for (int capability : BitUtils.unpackBits(Long.parseLong( netCapabilitiesLong))) { if (capability <= maxNetCapabilityInR) { builder.addCapability(capability); } for (int forbiddenCapability : BitUtils.unpackBits( Long.parseLong(netforbiddenCapabilities))) { } for (int forbiddenCapability : BitUtils.unpackBits(Long.parseLong( netForbiddenCapabilitiesLong))) { if (forbiddenCapability <= maxNetCapabilityInR) { builder.addForbiddenCapability(forbiddenCapability); } for (int transport : BitUtils.unpackBits(Long.parseLong(netTransportTypes))) { } final int maxTransportInR = TRANSPORT_TEST; for (int transport : BitUtils.unpackBits(Long.parseLong( netTransportTypesLong))) { if (transport <= maxTransportInR) { builder.addTransportType(transport); } } jobBuilder.setRequiredNetwork(builder.build()); } else { // Read legacy values Loading services/tests/servicestests/src/com/android/server/job/JobStoreTest.java +16 −0 Original line number Diff line number Diff line Loading @@ -4,7 +4,9 @@ import static android.net.NetworkCapabilities.NET_CAPABILITY_IMS; import static android.net.NetworkCapabilities.NET_CAPABILITY_OEM_PAID; import static android.net.NetworkCapabilities.TRANSPORT_WIFI; import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertThrows; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import static org.mockito.ArgumentMatchers.anyString; Loading Loading @@ -101,6 +103,20 @@ public class JobStoreTest { mTaskStoreUnderTest.waitForWriteToCompleteForTesting(5_000L)); } @Test public void testStringToIntArrayAndIntArrayToString() { final int[] netCapabilitiesIntArray = { 1, 3, 5, 7, 9 }; final String netCapabilitiesStr = "1,3,5,7,9"; final String netCapabilitiesStrWithErrorInt = "1,3,a,7,9"; final String emptyString = ""; final String str1 = JobStore.intArrayToString(netCapabilitiesIntArray); assertArrayEquals(netCapabilitiesIntArray, JobStore.stringToIntArray(str1)); assertEquals(0, JobStore.stringToIntArray(emptyString).length); assertThrows(NumberFormatException.class, () -> JobStore.stringToIntArray(netCapabilitiesStrWithErrorInt)); assertEquals(netCapabilitiesStr, JobStore.intArrayToString(netCapabilitiesIntArray)); } @Test public void testMaybeWriteStatusToDisk() throws Exception { int taskId = 5; Loading Loading
apex/jobscheduler/service/java/com/android/server/job/JobStore.java +110 −23 Original line number Diff line number Diff line Loading @@ -16,6 +16,9 @@ package com.android.server.job; import static android.net.NetworkCapabilities.NET_CAPABILITY_TEMPORARILY_NOT_METERED; import static android.net.NetworkCapabilities.TRANSPORT_TEST; import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock; import static com.android.server.job.JobSchedulerService.sSystemClock; Loading @@ -30,6 +33,7 @@ import android.os.PersistableBundle; import android.os.Process; import android.os.SystemClock; import android.os.UserHandle; import android.text.TextUtils; import android.text.format.DateUtils; import android.util.ArraySet; import android.util.AtomicFile; Loading Loading @@ -62,6 +66,7 @@ import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.List; import java.util.Set; import java.util.StringJoiner; import java.util.function.Consumer; import java.util.function.Predicate; Loading Loading @@ -383,6 +388,36 @@ public final class JobStore { return true; } /** * Returns a single string representation of the contents of the specified intArray. * If the intArray is [1, 2, 4] as the input, the return result will be the string "1,2,4". */ @VisibleForTesting static String intArrayToString(int[] values) { final StringJoiner sj = new StringJoiner(","); for (final int value : values) { sj.add(String.valueOf(value)); } return sj.toString(); } /** * Converts a string containing a comma-separated list of decimal representations * of ints into an array of int. If the string is not correctly formatted, * or if any value doesn't fit into an int, NumberFormatException is thrown. */ @VisibleForTesting static int[] stringToIntArray(String str) { if (TextUtils.isEmpty(str)) return new int[0]; final String[] arr = str.split(","); final int[] values = new int[arr.length]; for (int i = 0; i < arr.length; i++) { values[i] = Integer.parseInt(arr[i]); } return values; } /** * Runnable that writes {@link #mJobSet} out to xml. * NOTE: This Runnable locks on mLock Loading Loading @@ -540,15 +575,12 @@ public final class JobStore { out.startTag(null, XML_TAG_PARAMS_CONSTRAINTS); if (jobStatus.hasConnectivityConstraint()) { final NetworkRequest network = jobStatus.getJob().getRequiredNetwork(); // STOPSHIP b/183071974: improve the scheme for backward compatibility and // mainline cleanliness. out.attribute(null, "net-capabilities", Long.toString( BitUtils.packBits(network.getCapabilities()))); out.attribute(null, "net-unwanted-capabilities", Long.toString( BitUtils.packBits(network.getForbiddenCapabilities()))); out.attribute(null, "net-transport-types", Long.toString( BitUtils.packBits(network.getTransportTypes()))); out.attribute(null, "net-capabilities-csv", intArrayToString( network.getCapabilities())); out.attribute(null, "net-forbidden-capabilities-csv", intArrayToString( network.getForbiddenCapabilities())); out.attribute(null, "net-transport-types-csv", intArrayToString( network.getTransportTypes())); } if (jobStatus.hasIdleConstraint()) { out.attribute(null, "idle", Boolean.toString(true)); Loading Loading @@ -822,7 +854,14 @@ public final class JobStore { } catch (NumberFormatException e) { Slog.d(TAG, "Error reading constraints, skipping."); return null; } catch (XmlPullParserException e) { Slog.d(TAG, "Error Parser Exception.", e); return null; } catch (IOException e) { Slog.d(TAG, "Error I/O Exception.", e); return null; } parser.next(); // Consume </constraints> // Read out execution parameters tag. Loading Loading @@ -964,30 +1003,78 @@ public final class JobStore { return new JobInfo.Builder(jobId, cname); } private void buildConstraintsFromXml(JobInfo.Builder jobBuilder, XmlPullParser parser) { /** * In S, there has been a change in format to make the code more robust and more * maintainable. * If the capabities are bits 4, 14, 15, the format in R, it is a long string as * netCapabilitiesLong = '49168' from the old XML file attribute "net-capabilities". * The format in S is the int array string as netCapabilitiesIntArray = '4,14,15' * from the new XML file attribute "net-capabilities-array". * For backward compatibility, when reading old XML the old format is still supported in * reading, but in order to avoid issues with OEM-defined flags, the accepted capabilities * are limited to that(maxNetCapabilityInR & maxTransportInR) defined in R. */ private void buildConstraintsFromXml(JobInfo.Builder jobBuilder, XmlPullParser parser) throws XmlPullParserException, IOException { String val; final String netCapabilities = parser.getAttributeValue(null, "net-capabilities"); final String netforbiddenCapabilities = parser.getAttributeValue( String netCapabilitiesLong = null; String netForbiddenCapabilitiesLong = null; String netTransportTypesLong = null; final String netCapabilitiesIntArray = parser.getAttributeValue( null, "net-capabilities-csv"); final String netForbiddenCapabilitiesIntArray = parser.getAttributeValue( null, "net-forbidden-capabilities-csv"); final String netTransportTypesIntArray = parser.getAttributeValue( null, "net-transport-types-csv"); if (netCapabilitiesIntArray == null || netTransportTypesIntArray == null) { netCapabilitiesLong = parser.getAttributeValue(null, "net-capabilities"); netForbiddenCapabilitiesLong = parser.getAttributeValue( null, "net-unwanted-capabilities"); final String netTransportTypes = parser.getAttributeValue(null, "net-transport-types"); if (netCapabilities != null && netTransportTypes != null) { netTransportTypesLong = parser.getAttributeValue(null, "net-transport-types"); } if ((netCapabilitiesIntArray != null) && (netTransportTypesIntArray != null)) { final NetworkRequest.Builder builder = new NetworkRequest.Builder() .clearCapabilities(); final long forbiddenCapabilities = netforbiddenCapabilities != null ? Long.parseLong(netforbiddenCapabilities) : BitUtils.packBits(builder.build().getForbiddenCapabilities()); for (int capability : stringToIntArray(netCapabilitiesIntArray)) { builder.addCapability(capability); } for (int forbiddenCapability : stringToIntArray(netForbiddenCapabilitiesIntArray)) { builder.addForbiddenCapability(forbiddenCapability); } for (int transport : stringToIntArray(netTransportTypesIntArray)) { builder.addTransportType(transport); } jobBuilder.setRequiredNetwork(builder.build()); } else if (netCapabilitiesLong != null && netTransportTypesLong != null) { final NetworkRequest.Builder builder = new NetworkRequest.Builder() .clearCapabilities(); final int maxNetCapabilityInR = NET_CAPABILITY_TEMPORARILY_NOT_METERED; // We're okay throwing NFE here; caught by caller for (int capability : BitUtils.unpackBits(Long.parseLong(netCapabilities))) { for (int capability : BitUtils.unpackBits(Long.parseLong( netCapabilitiesLong))) { if (capability <= maxNetCapabilityInR) { builder.addCapability(capability); } for (int forbiddenCapability : BitUtils.unpackBits( Long.parseLong(netforbiddenCapabilities))) { } for (int forbiddenCapability : BitUtils.unpackBits(Long.parseLong( netForbiddenCapabilitiesLong))) { if (forbiddenCapability <= maxNetCapabilityInR) { builder.addForbiddenCapability(forbiddenCapability); } for (int transport : BitUtils.unpackBits(Long.parseLong(netTransportTypes))) { } final int maxTransportInR = TRANSPORT_TEST; for (int transport : BitUtils.unpackBits(Long.parseLong( netTransportTypesLong))) { if (transport <= maxTransportInR) { builder.addTransportType(transport); } } jobBuilder.setRequiredNetwork(builder.build()); } else { // Read legacy values Loading
services/tests/servicestests/src/com/android/server/job/JobStoreTest.java +16 −0 Original line number Diff line number Diff line Loading @@ -4,7 +4,9 @@ import static android.net.NetworkCapabilities.NET_CAPABILITY_IMS; import static android.net.NetworkCapabilities.NET_CAPABILITY_OEM_PAID; import static android.net.NetworkCapabilities.TRANSPORT_WIFI; import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertThrows; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import static org.mockito.ArgumentMatchers.anyString; Loading Loading @@ -101,6 +103,20 @@ public class JobStoreTest { mTaskStoreUnderTest.waitForWriteToCompleteForTesting(5_000L)); } @Test public void testStringToIntArrayAndIntArrayToString() { final int[] netCapabilitiesIntArray = { 1, 3, 5, 7, 9 }; final String netCapabilitiesStr = "1,3,5,7,9"; final String netCapabilitiesStrWithErrorInt = "1,3,a,7,9"; final String emptyString = ""; final String str1 = JobStore.intArrayToString(netCapabilitiesIntArray); assertArrayEquals(netCapabilitiesIntArray, JobStore.stringToIntArray(str1)); assertEquals(0, JobStore.stringToIntArray(emptyString).length); assertThrows(NumberFormatException.class, () -> JobStore.stringToIntArray(netCapabilitiesStrWithErrorInt)); assertEquals(netCapabilitiesStr, JobStore.intArrayToString(netCapabilitiesIntArray)); } @Test public void testMaybeWriteStatusToDisk() throws Exception { int taskId = 5; Loading