diff options
82 files changed, 1733 insertions, 332 deletions
diff --git a/StubLibraries.bp b/StubLibraries.bp index 626f9772655d..e194c47e8f9c 100644 --- a/StubLibraries.bp +++ b/StubLibraries.bp @@ -251,7 +251,7 @@ modules_public_stubs = [ modules_system_stubs = [ "android.net.ipsec.ike.stubs.system", - "art.module.public.api.stubs", // Only has public stubs + "art.module.public.api.stubs.system", "conscrypt.module.public.api.stubs", // Only has public stubs "framework-connectivity.stubs.system", "framework-media.stubs.system", diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobStore.java b/apex/jobscheduler/service/java/com/android/server/job/JobStore.java index bdab7d084a2a..858fe02d3327 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/JobStore.java +++ b/apex/jobscheduler/service/java/com/android/server/job/JobStore.java @@ -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; @@ -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; @@ -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; @@ -384,6 +389,36 @@ public final class JobStore { } /** + * 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 */ @@ -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)); @@ -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. @@ -964,31 +1003,79 @@ 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; + 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"); + netTransportTypesLong = parser.getAttributeValue(null, "net-transport-types"); + } - final String netCapabilities = parser.getAttributeValue(null, "net-capabilities"); - final String netforbiddenCapabilities = parser.getAttributeValue( - null, "net-unwanted-capabilities"); - final String netTransportTypes = parser.getAttributeValue(null, "net-transport-types"); - if (netCapabilities != null && netTransportTypes != null) { + 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()); - // We're okay throwing NFE here; caught by caller - for (int capability : BitUtils.unpackBits(Long.parseLong(netCapabilities))) { + + for (int capability : stringToIntArray(netCapabilitiesIntArray)) { builder.addCapability(capability); } - for (int forbiddenCapability : BitUtils.unpackBits( - Long.parseLong(netforbiddenCapabilities))) { + + for (int forbiddenCapability : stringToIntArray(netForbiddenCapabilitiesIntArray)) { builder.addForbiddenCapability(forbiddenCapability); } - for (int transport : BitUtils.unpackBits(Long.parseLong(netTransportTypes))) { + + 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( + netCapabilitiesLong))) { + if (capability <= maxNetCapabilityInR) { + builder.addCapability(capability); + } + } + for (int forbiddenCapability : BitUtils.unpackBits(Long.parseLong( + netForbiddenCapabilitiesLong))) { + if (forbiddenCapability <= maxNetCapabilityInR) { + builder.addForbiddenCapability(forbiddenCapability); + } + } + + 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 val = parser.getAttributeValue(null, "connectivity"); diff --git a/api/Android.bp b/api/Android.bp index 5b2d97f11378..438e7dc29b22 100644 --- a/api/Android.bp +++ b/api/Android.bp @@ -149,6 +149,7 @@ genrule { genrule { name: "frameworks-base-api-system-current.txt", srcs: [ + ":art.module.public.api{.system.api.txt}", ":android.net.ipsec.ike{.system.api.txt}", ":framework-connectivity{.system.api.txt}", ":framework-media{.system.api.txt}", @@ -199,6 +200,7 @@ genrule { genrule { name: "frameworks-base-api-system-removed.txt", srcs: [ + ":art.module.public.api{.system.removed-api.txt}", ":android.net.ipsec.ike{.system.removed-api.txt}", ":framework-connectivity{.system.removed-api.txt}", ":framework-media{.system.removed-api.txt}", @@ -231,6 +233,7 @@ genrule { genrule { name: "frameworks-base-api-module-lib-current.txt", srcs: [ + ":art.module.public.api{.module-lib.api.txt}", ":android.net.ipsec.ike{.module-lib.api.txt}", ":framework-connectivity{.module-lib.api.txt}", ":framework-media{.module-lib.api.txt}", @@ -283,6 +286,7 @@ genrule { genrule { name: "frameworks-base-api-module-lib-removed.txt", srcs: [ + ":art.module.public.api{.module-lib.removed-api.txt}", ":android.net.ipsec.ike{.module-lib.removed-api.txt}", ":framework-connectivity{.module-lib.removed-api.txt}", ":framework-media{.module-lib.removed-api.txt}", diff --git a/boot/Android.bp b/boot/Android.bp index 3caede47d859..09cd0d4b6fe6 100644 --- a/boot/Android.bp +++ b/boot/Android.bp @@ -52,9 +52,45 @@ platform_bootclasspath { module: "art-bootclasspath-fragment", }, { + apex: "com.android.conscrypt", + module: "com.android.conscrypt-bootclasspath-fragment", + }, + { apex: "com.android.i18n", module: "i18n-bootclasspath-fragment", }, + { + apex: "com.android.ipsec", + module: "com.android.ipsec-bootclasspath-fragment", + }, + { + apex: "com.android.media", + module: "com.android.media-bootclasspath-fragment", + }, + { + apex: "com.android.mediaprovider", + module: "com.android.mediaprovider-bootclasspath-fragment", + }, + { + apex: "com.android.os.statsd", + module: "com.android.os.statsd-bootclasspath-fragment", + }, + { + apex: "com.android.permission", + module: "com.android.permission-bootclasspath-fragment", + }, + { + apex: "com.android.sdkext", + module: "com.android.sdkext-bootclasspath-fragment", + }, + { + apex: "com.android.tethering", + module: "com.android.tethering-bootclasspath-fragment", + }, + { + apex: "com.android.wifi", + module: "com.android.wifi-bootclasspath-fragment", + }, ], // Additional information needed by hidden api processing. diff --git a/cmds/idmap2/OWNERS b/cmds/idmap2/OWNERS index f1903a5a54a7..69dfcc98340d 100644 --- a/cmds/idmap2/OWNERS +++ b/cmds/idmap2/OWNERS @@ -1,3 +1,4 @@ set noparent toddke@google.com -rtmitchell@google.com
\ No newline at end of file +rtmitchell@google.com +patb@google.com
\ No newline at end of file diff --git a/core/api/current.txt b/core/api/current.txt index cd4e6f9a826c..e32f07b53187 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -25711,7 +25711,7 @@ package android.net.vcn { public final class VcnGatewayConnectionConfig { method @NonNull public int[] getExposedCapabilities(); method @NonNull public String getGatewayConnectionName(); - method @IntRange(from=android.net.vcn.VcnGatewayConnectionConfig.MIN_MTU_V6) public int getMaxMtu(); + method @IntRange(from=0x500) public int getMaxMtu(); method @NonNull public long[] getRetryIntervalsMillis(); } @@ -25720,7 +25720,7 @@ package android.net.vcn { method @NonNull public android.net.vcn.VcnGatewayConnectionConfig.Builder addExposedCapability(int); method @NonNull public android.net.vcn.VcnGatewayConnectionConfig build(); method @NonNull public android.net.vcn.VcnGatewayConnectionConfig.Builder removeExposedCapability(int); - method @NonNull public android.net.vcn.VcnGatewayConnectionConfig.Builder setMaxMtu(@IntRange(from=android.net.vcn.VcnGatewayConnectionConfig.MIN_MTU_V6) int); + method @NonNull public android.net.vcn.VcnGatewayConnectionConfig.Builder setMaxMtu(@IntRange(from=0x500) int); method @NonNull public android.net.vcn.VcnGatewayConnectionConfig.Builder setRetryIntervalsMillis(@NonNull long[]); } diff --git a/core/api/system-current.txt b/core/api/system-current.txt index bb42ddcda37f..1aa8aed7c7ec 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -1124,10 +1124,10 @@ package android.app.backup { package android.app.compat { public final class CompatChanges { - method @RequiresPermission(android.Manifest.permission.OVERRIDE_COMPAT_CHANGE_CONFIG_ON_RELEASE_BUILD) public static void addPackageOverrides(@NonNull String, @NonNull java.util.Map<java.lang.Long,android.app.compat.PackageOverride>); method public static boolean isChangeEnabled(long); method @RequiresPermission(allOf={"android.permission.READ_COMPAT_CHANGE_CONFIG", "android.permission.LOG_COMPAT_CHANGE"}) public static boolean isChangeEnabled(long, @NonNull String, @NonNull android.os.UserHandle); method @RequiresPermission(allOf={"android.permission.READ_COMPAT_CHANGE_CONFIG", "android.permission.LOG_COMPAT_CHANGE"}) public static boolean isChangeEnabled(long, int); + method @RequiresPermission(android.Manifest.permission.OVERRIDE_COMPAT_CHANGE_CONFIG_ON_RELEASE_BUILD) public static void putPackageOverrides(@NonNull String, @NonNull java.util.Map<java.lang.Long,android.app.compat.PackageOverride>); method @RequiresPermission(android.Manifest.permission.OVERRIDE_COMPAT_CHANGE_CONFIG_ON_RELEASE_BUILD) public static void removePackageOverrides(@NonNull String, @NonNull java.util.Set<java.lang.Long>); } diff --git a/core/java/android/app/DexLoadReporter.java b/core/java/android/app/DexLoadReporter.java index 5bc999240a66..a172c058e091 100644 --- a/core/java/android/app/DexLoadReporter.java +++ b/core/java/android/app/DexLoadReporter.java @@ -138,23 +138,25 @@ import java.util.Set; // NOTE: Keep this in sync with installd expectations. File dexPathFile = new File(dexPath); File secondaryProfileDir = new File(dexPathFile.getParent(), "oat"); - File secondaryProfile = new File(secondaryProfileDir, dexPathFile.getName() + ".cur.prof"); + File secondaryCurProfile = + new File(secondaryProfileDir, dexPathFile.getName() + ".cur.prof"); + File secondaryRefProfile = new File(secondaryProfileDir, dexPathFile.getName() + ".prof"); // Create the profile if not already there. // Returns true if the file was created, false if the file already exists. // or throws exceptions in case of errors. if (!secondaryProfileDir.exists()) { if (!secondaryProfileDir.mkdir()) { - Slog.e(TAG, "Could not create the profile directory: " + secondaryProfile); + Slog.e(TAG, "Could not create the profile directory: " + secondaryCurProfile); // Do not continue with registration if we could not create the oat dir. return; } } try { - boolean created = secondaryProfile.createNewFile(); + boolean created = secondaryCurProfile.createNewFile(); if (DEBUG && created) { - Slog.i(TAG, "Created profile for secondary dex: " + secondaryProfile); + Slog.i(TAG, "Created profile for secondary dex: " + secondaryCurProfile); } } catch (IOException ex) { Slog.e(TAG, "Failed to create profile for secondary dex " + dexPath @@ -165,7 +167,12 @@ import java.util.Set; // If we got here, the dex paths is a secondary dex and we were able to create the profile. // Register the path to the runtime. - VMRuntime.registerAppInfo(secondaryProfile.getPath(), new String[] { dexPath }); + VMRuntime.registerAppInfo( + ActivityThread.currentPackageName(), + secondaryCurProfile.getPath(), + secondaryRefProfile.getPath(), + new String[] { dexPath }, + VMRuntime.CODE_PATH_TYPE_SECONDARY_DEX); } // A dex file is a secondary dex file if it is in any of the registered app diff --git a/core/java/android/app/LoadedApk.java b/core/java/android/app/LoadedApk.java index b45f3893db8c..5d2370db8e88 100644 --- a/core/java/android/app/LoadedApk.java +++ b/core/java/android/app/LoadedApk.java @@ -884,7 +884,7 @@ public final class LoadedApk { if (DEBUG) Slog.v(ActivityThread.TAG, "Class path: " + zip + ", JNI path: " + librarySearchPath); - boolean needToSetupJitProfiles = false; + boolean registerAppInfoToArt = false; if (mDefaultClassLoader == null) { // Temporarily disable logging of disk reads on the Looper thread // as this is early and necessary. @@ -902,7 +902,7 @@ public final class LoadedApk { setThreadPolicy(oldPolicy); // Setup the class loader paths for profiling. - needToSetupJitProfiles = true; + registerAppInfoToArt = true; } if (!libPaths.isEmpty()) { @@ -919,7 +919,7 @@ public final class LoadedApk { final String add = TextUtils.join(File.pathSeparator, addedPaths); ApplicationLoaders.getDefault().addPath(mDefaultClassLoader, add); // Setup the new code paths for profiling. - needToSetupJitProfiles = true; + registerAppInfoToArt = true; } // Setup jit profile support. @@ -933,8 +933,8 @@ public final class LoadedApk { // loads code from) so we explicitly disallow it there. // // It is not ok to call this in a zygote context where mActivityThread is null. - if (needToSetupJitProfiles && !ActivityThread.isSystem() && mActivityThread != null) { - setupJitProfileSupport(); + if (registerAppInfoToArt && !ActivityThread.isSystem() && mActivityThread != null) { + registerAppInfoToArt(); } // Call AppComponentFactory to select/create the main class loader of this app. @@ -984,12 +984,8 @@ public final class LoadedApk { } } - private void setupJitProfileSupport() { - if (!SystemProperties.getBoolean("dalvik.vm.usejitprofiles", false)) { - return; - } - - // If we use profiles, setup the dex reporter to notify package manager + private void registerAppInfoToArt() { + // Setup the dex reporter to notify package manager // of any relevant dex loads. The idle maintenance job will use the information // reported to optimize the loaded dex files. // Note that we only need one global reporter per app. @@ -1022,9 +1018,19 @@ public final class LoadedApk { for (int i = codePaths.size() - 1; i >= 0; i--) { String splitName = i == 0 ? null : mApplicationInfo.splitNames[i - 1]; - String profileFile = ArtManager.getCurrentProfilePath( + String curProfileFile = ArtManager.getCurrentProfilePath( + mPackageName, UserHandle.myUserId(), splitName); + String refProfileFile = ArtManager.getReferenceProfilePath( mPackageName, UserHandle.myUserId(), splitName); - VMRuntime.registerAppInfo(profileFile, new String[] {codePaths.get(i)}); + int codePathType = codePaths.get(i).equals(mApplicationInfo.sourceDir) + ? VMRuntime.CODE_PATH_TYPE_PRIMARY_APK + : VMRuntime.CODE_PATH_TYPE_SPLIT_APK; + VMRuntime.registerAppInfo( + mPackageName, + curProfileFile, + refProfileFile, + new String[] {codePaths.get(i)}, + codePathType); } // Register the app data directory with the reporter. It will diff --git a/core/java/android/app/RESOURCES_OWNERS b/core/java/android/app/RESOURCES_OWNERS index 21c39a8828ad..558280396348 100644 --- a/core/java/android/app/RESOURCES_OWNERS +++ b/core/java/android/app/RESOURCES_OWNERS @@ -1,2 +1,3 @@ rtmitchell@google.com toddke@google.com +patb@google.com diff --git a/core/java/android/app/compat/CompatChanges.java b/core/java/android/app/compat/CompatChanges.java index 8ca43c4a8e70..24ca72c66ab4 100644 --- a/core/java/android/app/compat/CompatChanges.java +++ b/core/java/android/app/compat/CompatChanges.java @@ -100,9 +100,10 @@ public final class CompatChanges { } /** - * Adds app compat overrides for a given package. This will check whether the caller is allowed - * to perform this operation on the given apk and build. Only the installer package is allowed - * to set overrides on a non-debuggable final build and a non-test apk. + * Associates app compat overrides with the given package and their respective change IDs. + * This will check whether the caller is allowed to perform this operation on the given apk and + * build. Only the installer package is allowed to set overrides on a non-debuggable final + * build and a non-test apk. * * <p>Note that calling this method doesn't remove previously added overrides for the given * package if their change ID isn't in the given map, only replaces those that have the same @@ -112,7 +113,7 @@ public final class CompatChanges { * @param overrides A map from change ID to the override applied for this change ID. */ @RequiresPermission(android.Manifest.permission.OVERRIDE_COMPAT_CHANGE_CONFIG_ON_RELEASE_BUILD) - public static void addPackageOverrides(@NonNull String packageName, + public static void putPackageOverrides(@NonNull String packageName, @NonNull Map<Long, PackageOverride> overrides) { IPlatformCompat platformCompat = IPlatformCompat.Stub.asInterface( ServiceManager.getService(Context.PLATFORM_COMPAT_SERVICE)); @@ -125,7 +126,7 @@ public final class CompatChanges { } /** - * Removes app compat overrides for a given package. This will check whether the caller is + * Removes app compat overrides for the given package. This will check whether the caller is * allowed to perform this operation on the given apk and build. Only the installer package is * allowed to clear overrides on a non-debuggable final build and a non-test apk. * diff --git a/core/java/android/app/usage/NetworkStatsManager.java b/core/java/android/app/usage/NetworkStatsManager.java index 6cb4b5e9b3d4..fe99f8532aaa 100644 --- a/core/java/android/app/usage/NetworkStatsManager.java +++ b/core/java/android/app/usage/NetworkStatsManager.java @@ -644,7 +644,10 @@ public class NetworkStatsManager { : NetworkTemplate.buildTemplateMobileAll(subscriberId); break; case ConnectivityManager.TYPE_WIFI: - template = NetworkTemplate.buildTemplateWifiWildcard(); + template = subscriberId == null + ? NetworkTemplate.buildTemplateWifiWildcard() + : NetworkTemplate.buildTemplateWifi(NetworkTemplate.WIFI_NETWORKID_ALL, + subscriberId); break; default: throw new IllegalArgumentException("Cannot create template for network type " diff --git a/core/java/android/content/pm/dex/ArtManager.java b/core/java/android/content/pm/dex/ArtManager.java index b0970f4878db..009ebb9b4b96 100644 --- a/core/java/android/content/pm/dex/ArtManager.java +++ b/core/java/android/content/pm/dex/ArtManager.java @@ -201,6 +201,16 @@ public class ArtManager { } /** + * Return the path to the current profile corresponding to given package and split. + * + * @hide + */ + public static String getReferenceProfilePath(String packageName, int userId, String splitName) { + File profileDir = Environment.getDataRefProfilesDePackageDirectory(packageName); + return new File(profileDir, getProfileName(splitName)).getAbsolutePath(); + } + + /** * Return the snapshot profile file for the given package and profile name. * * KEEP in sync with installd dexopt.cpp. diff --git a/core/java/android/content/pm/parsing/result/ParseTypeImpl.java b/core/java/android/content/pm/parsing/result/ParseTypeImpl.java index 14992fb2a4d1..3740ef7d5a92 100644 --- a/core/java/android/content/pm/parsing/result/ParseTypeImpl.java +++ b/core/java/android/content/pm/parsing/result/ParseTypeImpl.java @@ -119,6 +119,7 @@ public class ParseTypeImpl implements ParseInput, ParseResult<Object> { // how many APKs they're going through. mDeferredErrors.erase(); } + mTargetSdkVersion = null; return this; } diff --git a/core/java/android/net/IpSecManager.java b/core/java/android/net/IpSecManager.java index 01d1aa533a8f..c10680761ff1 100644 --- a/core/java/android/net/IpSecManager.java +++ b/core/java/android/net/IpSecManager.java @@ -810,7 +810,9 @@ public final class IpSecManager { * * @param underlyingNetwork the new {@link Network} that will carry traffic for this tunnel. * This network MUST never be the network exposing this IpSecTunnelInterface, otherwise - * this method will throw an {@link IllegalArgumentException}. + * this method will throw an {@link IllegalArgumentException}. If the + * IpSecTunnelInterface is later added to this network, all outbound traffic will be + * blackholed. */ // TODO: b/169171001 Update the documentation when transform migration is supported. // The purpose of making updating network and applying transforms separate is to leave open diff --git a/core/java/android/net/NetworkIdentity.java b/core/java/android/net/NetworkIdentity.java index 3bde6fa6913d..1d07a0330bc5 100644 --- a/core/java/android/net/NetworkIdentity.java +++ b/core/java/android/net/NetworkIdentity.java @@ -26,8 +26,10 @@ import android.service.NetworkIdentityProto; import android.telephony.Annotation.NetworkType; import android.util.proto.ProtoOutputStream; +import com.android.net.module.util.NetworkCapabilitiesUtils; import com.android.net.module.util.NetworkIdentityUtils; +import java.util.ArrayList; import java.util.Objects; /** @@ -121,11 +123,37 @@ public class NetworkIdentity implements Comparable<NetworkIdentity> { } builder.append(", metered=").append(mMetered); builder.append(", defaultNetwork=").append(mDefaultNetwork); - // TODO(180557699): Print a human readable string for OEM managed state. - builder.append(", oemManaged=").append(mOemManaged); + builder.append(", oemManaged=").append(getOemManagedNames(mOemManaged)); return builder.append("}").toString(); } + /** + * Get the human readable representation of a bitfield representing the OEM managed state of a + * network. + */ + static String getOemManagedNames(int oemManaged) { + if (oemManaged == OEM_NONE) { + return "OEM_NONE"; + } + final int[] bitPositions = NetworkCapabilitiesUtils.unpackBits(oemManaged); + final ArrayList<String> oemManagedNames = new ArrayList<String>(); + for (int position : bitPositions) { + oemManagedNames.add(nameOfOemManaged(1 << position)); + } + return String.join(",", oemManagedNames); + } + + private static String nameOfOemManaged(int oemManagedBit) { + switch (oemManagedBit) { + case OEM_PAID: + return "OEM_PAID"; + case OEM_PRIVATE: + return "OEM_PRIVATE"; + default: + return "Invalid(" + oemManagedBit + ")"; + } + } + public void dumpDebug(ProtoOutputStream proto, long tag) { final long start = proto.start(tag); diff --git a/core/java/android/net/NetworkTemplate.java b/core/java/android/net/NetworkTemplate.java index d3c89574944f..352f2e99aa5e 100644 --- a/core/java/android/net/NetworkTemplate.java +++ b/core/java/android/net/NetworkTemplate.java @@ -274,11 +274,14 @@ public class NetworkTemplate implements Parcelable { } /** - * Template to match all carrier networks with the given IMSI. + * Template to match all metered carrier networks with the given IMSI. */ - public static NetworkTemplate buildTemplateCarrier(@NonNull String subscriberId) { + public static NetworkTemplate buildTemplateCarrierMetered(@NonNull String subscriberId) { Objects.requireNonNull(subscriberId); - return new NetworkTemplate(MATCH_CARRIER, subscriberId, null); + return new NetworkTemplate(MATCH_CARRIER, subscriberId, + new String[] { subscriberId }, null /* networkId */, METERED_YES, ROAMING_ALL, + DEFAULT_NETWORK_ALL, NETWORK_TYPE_ALL, OEM_MANAGED_ALL, + SUBSCRIBER_ID_MATCH_RULE_EXACT); } private final int mMatchRule; @@ -424,7 +427,7 @@ public class NetworkTemplate implements Parcelable { builder.append(", subType=").append(mSubType); } if (mOemManaged != OEM_MANAGED_ALL) { - builder.append(", oemManaged=").append(mOemManaged); + builder.append(", oemManaged=").append(getOemManagedNames(mOemManaged)); } builder.append(", subscriberIdMatchRule=") .append(subscriberIdMatchRuleToString(mSubscriberIdMatchRule)); @@ -774,6 +777,19 @@ public class NetworkTemplate implements Parcelable { } } + private static String getOemManagedNames(int oemManaged) { + switch (oemManaged) { + case OEM_MANAGED_ALL: + return "OEM_MANAGED_ALL"; + case OEM_MANAGED_NO: + return "OEM_MANAGED_NO"; + case OEM_MANAGED_YES: + return "OEM_MANAGED_YES"; + default: + return NetworkIdentity.getOemManagedNames(oemManaged); + } + } + /** * Examine the given template and normalize if it refers to a "merged" * mobile subscriber. We pick the "lowest" merged subscriber as the primary diff --git a/core/java/android/os/PersistableBundle.java b/core/java/android/os/PersistableBundle.java index 7a837e167fb0..339371b5047c 100644 --- a/core/java/android/os/PersistableBundle.java +++ b/core/java/android/os/PersistableBundle.java @@ -302,7 +302,7 @@ public final class PersistableBundle extends BaseBundle implements Cloneable, Pa new MyReadMapCallback())); } } - return EMPTY; + return new PersistableBundle(); // An empty mutable PersistableBundle } @Override diff --git a/core/java/android/os/incremental/OWNERS b/core/java/android/os/incremental/OWNERS index 3795493b861f..47eee6406206 100644 --- a/core/java/android/os/incremental/OWNERS +++ b/core/java/android/os/incremental/OWNERS @@ -3,3 +3,4 @@ alexbuy@google.com schfan@google.com toddke@google.com zyy@google.com +patb@google.com diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java index c34b9f09ecaa..dd2940f8c110 100644 --- a/core/java/com/android/internal/os/BatteryStatsImpl.java +++ b/core/java/com/android/internal/os/BatteryStatsImpl.java @@ -40,6 +40,7 @@ import android.net.Uri; import android.net.wifi.WifiManager; import android.os.BatteryManager; import android.os.BatteryStats; +import android.os.Binder; import android.os.Build; import android.os.Handler; import android.os.IBatteryPropertiesRegistrar; @@ -71,6 +72,7 @@ import android.telephony.SignalStrength; import android.telephony.TelephonyManager; import android.text.TextUtils; import android.util.ArrayMap; +import android.util.ArraySet; import android.util.AtomicFile; import android.util.IntArray; import android.util.KeyValueListParser; @@ -122,6 +124,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Calendar; import java.util.Collection; +import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; @@ -6126,6 +6129,18 @@ public class BatteryStatsImpl extends BatteryStats { } } + /** + * Records timing data related to an incoming Binder call in order to attribute + * the power consumption to the calling app. + */ + public void noteBinderCallStats(int workSourceUid, long incrementalCallCount, + Collection<BinderCallsStats.CallStat> callStats) { + synchronized (this) { + getUidStatsLocked(workSourceUid).noteBinderCallStatsLocked(incrementalCallCount, + callStats); + } + } + public String[] getWifiIfaces() { synchronized (mWifiNetworkLock) { return mWifiIfaces; @@ -6569,6 +6584,65 @@ public class BatteryStatsImpl extends BatteryStats { } /** + * Accumulates stats for a specific binder transaction. + */ + @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) + protected static class BinderCallStats { + static final Comparator<BinderCallStats> COMPARATOR = + Comparator.comparing(BinderCallStats::getClassName) + .thenComparing(BinderCallStats::getMethodName); + + public Class<? extends Binder> binderClass; + public int transactionCode; + public String methodName; + + public long callCount; + public long recordedCallCount; + public long recordedCpuTimeMicros; + + + @Override + public int hashCode() { + return binderClass.hashCode() * 31 + transactionCode; + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof BinderCallStats)) { + return false; + } + BinderCallStats bcsk = (BinderCallStats) obj; + return binderClass.equals(bcsk.binderClass) && transactionCode == bcsk.transactionCode; + } + + public String getClassName() { + return binderClass.getName(); + } + + public String getMethodName() { + return methodName; + } + + @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) + public void ensureMethodName(BinderTransactionNameResolver resolver) { + if (methodName == null) { + methodName = resolver.getMethodName(binderClass, transactionCode); + } + } + + @Override + public String toString() { + return "BinderCallStats{" + + binderClass + + " transaction=" + transactionCode + + " callCount=" + callCount + + " recordedCallCount=" + recordedCallCount + + " recorderCpuTimeMicros=" + recordedCpuTimeMicros + + "}"; + } + } + + /** * The statistics associated with a particular uid. */ public static class Uid extends BatteryStats.Uid { @@ -6741,6 +6815,16 @@ public class BatteryStatsImpl extends BatteryStats { */ final SparseArray<Pid> mPids = new SparseArray<>(); + /** + * Grand total of system server binder calls made by this uid. + */ + private long mBinderCallCount; + + /** + * Detailed information about system server binder calls made by this uid. + */ + private final ArraySet<BinderCallStats> mBinderCallStats = new ArraySet<>(); + public Uid(BatteryStatsImpl bsi, int uid) { mBsi = bsi; mUid = uid; @@ -6849,6 +6933,14 @@ public class BatteryStatsImpl extends BatteryStats { return nullIfAllZeros(mProcStateScreenOffTimeMs[procState], which); } + public long getBinderCallCount() { + return mBinderCallCount; + } + + public ArraySet<BinderCallStats> getBinderCallStats() { + return mBinderCallStats; + } + public void addIsolatedUid(int isolatedUid) { if (mChildUids == null) { mChildUids = new IntArray(); @@ -7937,6 +8029,9 @@ public class BatteryStatsImpl extends BatteryStats { } mPackageStats.clear(); + mBinderCallCount = 0; + mBinderCallStats.clear(); + mLastStepUserTime = mLastStepSystemTime = 0; mCurStepUserTime = mCurStepSystemTime = 0; @@ -8692,6 +8787,40 @@ public class BatteryStatsImpl extends BatteryStats { } } + // Reusable object used as a key to lookup values in mBinderCallStats + private static BinderCallStats sTempBinderCallStats = new BinderCallStats(); + + /** + * Notes incoming binder call stats associated with this work source UID. + */ + public void noteBinderCallStatsLocked(long incrementalCallCount, + Collection<BinderCallsStats.CallStat> callStats) { + if (DEBUG) { + Slog.d(TAG, "noteBinderCalls() workSourceUid = [" + mUid + "], " + + " incrementalCallCount: " + incrementalCallCount + " callStats = [" + + new ArrayList<>(callStats) + "]"); + } + mBinderCallCount += incrementalCallCount; + for (BinderCallsStats.CallStat stat : callStats) { + BinderCallStats bcs; + sTempBinderCallStats.binderClass = stat.binderClass; + sTempBinderCallStats.transactionCode = stat.transactionCode; + int index = mBinderCallStats.indexOf(sTempBinderCallStats); + if (index >= 0) { + bcs = mBinderCallStats.valueAt(index); + } else { + bcs = new BinderCallStats(); + bcs.binderClass = stat.binderClass; + bcs.transactionCode = stat.transactionCode; + mBinderCallStats.add(bcs); + } + + bcs.callCount += stat.incrementalCallCount; + bcs.recordedCallCount = stat.recordedCallCount; + bcs.recordedCpuTimeMicros = stat.cpuTimeMicros; + } + } + /** * The statistics associated with a particular wake lock. */ @@ -13213,6 +13342,45 @@ public class BatteryStatsImpl extends BatteryStats { pw.print(uid.getUserCpuTimeUs(STATS_SINCE_CHARGED) / 1000); pw.print(" "); pw.println(uid.getSystemCpuTimeUs(STATS_SINCE_CHARGED) / 1000); } + pw.println("Per UID system service calls:"); + BinderTransactionNameResolver nameResolver = new BinderTransactionNameResolver(); + for (int i = 0; i < size; i++) { + int u = mUidStats.keyAt(i); + Uid uid = mUidStats.get(u); + long binderCallCount = uid.getBinderCallCount(); + if (binderCallCount != 0) { + pw.print(" "); + pw.print(u); + pw.print(" system service calls: "); + pw.print(binderCallCount); + ArraySet<BinderCallStats> binderCallStats = uid.getBinderCallStats(); + if (!binderCallStats.isEmpty()) { + pw.println(", including"); + BinderCallStats[] bcss = new BinderCallStats[binderCallStats.size()]; + binderCallStats.toArray(bcss); + for (BinderCallStats bcs : bcss) { + bcs.ensureMethodName(nameResolver); + } + Arrays.sort(bcss, BinderCallStats.COMPARATOR); + for (BinderCallStats callStats : bcss) { + pw.print(" "); + pw.print(callStats.getClassName()); + pw.print('#'); + pw.print(callStats.getMethodName()); + pw.print(" calls: "); + pw.print(callStats.callCount); + if (callStats.recordedCallCount != 0) { + pw.print(" time: "); + pw.print(callStats.callCount * callStats.recordedCpuTimeMicros + / callStats.recordedCallCount / 1000); + } + pw.println(); + } + } else { + pw.println(); + } + } + } pw.println("Per UID CPU active time in ms:"); for (int i = 0; i < size; i++) { int u = mUidStats.keyAt(i); diff --git a/core/java/com/android/internal/os/BinderCallsStats.java b/core/java/com/android/internal/os/BinderCallsStats.java index dbba469dda1a..a3dbd30d089e 100644 --- a/core/java/com/android/internal/os/BinderCallsStats.java +++ b/core/java/com/android/internal/os/BinderCallsStats.java @@ -19,10 +19,14 @@ package com.android.internal.os; import android.annotation.NonNull; import android.annotation.Nullable; import android.os.Binder; +import android.os.Handler; +import android.os.Looper; import android.os.Process; import android.os.SystemClock; +import android.os.UserHandle; import android.text.format.DateFormat; import android.util.ArrayMap; +import android.util.ArraySet; import android.util.Pair; import android.util.Slog; import android.util.SparseArray; @@ -32,13 +36,10 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.internal.os.BinderInternal.CallSession; import java.io.PrintWriter; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Collection; import java.util.Comparator; import java.util.List; -import java.util.Map; import java.util.Queue; import java.util.Random; import java.util.concurrent.ConcurrentLinkedQueue; @@ -95,7 +96,38 @@ public class BinderCallsStats implements BinderInternal.Observer { private CachedDeviceState.Readonly mDeviceState; private CachedDeviceState.TimeInStateStopwatch mBatteryStopwatch; + private static final int CALL_STATS_OBSERVER_DEBOUNCE_MILLIS = 5000; private BinderLatencyObserver mLatencyObserver; + private BinderInternal.CallStatsObserver mCallStatsObserver; + private ArraySet<Integer> mSendUidsToObserver = new ArraySet<>(32); + private final Handler mCallStatsObserverHandler; + private Runnable mCallStatsObserverRunnable = new Runnable() { + @Override + public void run() { + if (mCallStatsObserver == null) { + return; + } + + noteCallsStatsDelayed(); + + synchronized (mLock) { + int size = mSendUidsToObserver.size(); + for (int i = 0; i < size; i++) { + UidEntry uidEntry = mUidEntries.get(mSendUidsToObserver.valueAt(i)); + if (uidEntry != null) { + ArrayMap<CallStatKey, CallStat> callStats = uidEntry.mCallStats; + mCallStatsObserver.noteCallStats(uidEntry.workSourceUid, + uidEntry.incrementalCallCount, callStats.values()); + uidEntry.incrementalCallCount = 0; + for (int j = callStats.size() - 1; j >= 0; j--) { + callStats.valueAt(j).incrementalCallCount = 0; + } + } + } + mSendUidsToObserver.clear(); + } + } + }; /** Injector for {@link BinderCallsStats}. */ public static class Injector { @@ -103,6 +135,10 @@ public class BinderCallsStats implements BinderInternal.Observer { return new Random(); } + public Handler getHandler() { + return new Handler(Looper.getMainLooper()); + } + public BinderLatencyObserver getLatencyObserver() { return new BinderLatencyObserver(new BinderLatencyObserver.Injector()); } @@ -110,6 +146,7 @@ public class BinderCallsStats implements BinderInternal.Observer { public BinderCallsStats(Injector injector) { this.mRandom = injector.getRandomGenerator(); + this.mCallStatsObserverHandler = injector.getHandler(); this.mLatencyObserver = injector.getLatencyObserver(); } @@ -121,6 +158,24 @@ public class BinderCallsStats implements BinderInternal.Observer { mBatteryStopwatch = deviceState.createTimeOnBatteryStopwatch(); } + /** + * Registers an observer for call stats, which is invoked periodically with accumulated + * binder call stats. + */ + public void setCallStatsObserver( + BinderInternal.CallStatsObserver callStatsObserver) { + mCallStatsObserver = callStatsObserver; + noteCallsStatsDelayed(); + } + + private void noteCallsStatsDelayed() { + mCallStatsObserverHandler.removeCallbacks(mCallStatsObserverRunnable); + if (mCallStatsObserver != null) { + mCallStatsObserverHandler.postDelayed(mCallStatsObserverRunnable, + CALL_STATS_OBSERVER_DEBOUNCE_MILLIS); + } + } + @Override @Nullable public CallSession callStarted(Binder binder, int code, int workSourceUid) { @@ -195,6 +250,7 @@ public class BinderCallsStats implements BinderInternal.Observer { final UidEntry uidEntry = getUidEntry(workSourceUid); uidEntry.callCount++; + uidEntry.incrementalCallCount++; if (recordCall) { uidEntry.cpuTimeMicros += duration; @@ -210,6 +266,7 @@ public class BinderCallsStats implements BinderInternal.Observer { } callStat.callCount++; + callStat.incrementalCallCount++; callStat.recordedCallCount++; callStat.cpuTimeMicros += duration; callStat.maxCpuTimeMicros = Math.max(callStat.maxCpuTimeMicros, duration); @@ -231,8 +288,12 @@ public class BinderCallsStats implements BinderInternal.Observer { screenInteractive); if (callStat != null) { callStat.callCount++; + callStat.incrementalCallCount++; } } + if (mCallStatsObserver != null && !UserHandle.isCore(workSourceUid)) { + mSendUidsToObserver.add(workSourceUid); + } } } @@ -266,29 +327,6 @@ public class BinderCallsStats implements BinderInternal.Observer { } } - @Nullable - private Method getDefaultTransactionNameMethod(Class<? extends Binder> binder) { - try { - return binder.getMethod("getDefaultTransactionName", int.class); - } catch (NoSuchMethodException e) { - // The method might not be present for stubs not generated with AIDL. - return null; - } - } - - @Nullable - private String resolveTransactionCode(Method getDefaultTransactionName, int transactionCode) { - if (getDefaultTransactionName == null) { - return null; - } - - try { - return (String) getDefaultTransactionName.invoke(null, transactionCode); - } catch (IllegalAccessException | InvocationTargetException | ClassCastException e) { - throw new RuntimeException(e); - } - } - /** * This method is expensive to call. */ @@ -327,31 +365,23 @@ public class BinderCallsStats implements BinderInternal.Observer { // Resolve codes outside of the lock since it can be slow. ExportedCallStat previous = null; - // Cache the previous method/transaction code. - Method getDefaultTransactionName = null; String previousMethodName = null; resultCallStats.sort(BinderCallsStats::compareByBinderClassAndCode); + BinderTransactionNameResolver resolver = new BinderTransactionNameResolver(); for (ExportedCallStat exported : resultCallStats) { final boolean isClassDifferent = previous == null || !previous.className.equals(exported.className); - if (isClassDifferent) { - getDefaultTransactionName = getDefaultTransactionNameMethod(exported.binderClass); - } - final boolean isCodeDifferent = previous == null || previous.transactionCode != exported.transactionCode; final String methodName; if (isClassDifferent || isCodeDifferent) { - String resolvedCode = resolveTransactionCode( - getDefaultTransactionName, exported.transactionCode); - methodName = resolvedCode == null - ? String.valueOf(exported.transactionCode) - : resolvedCode; + methodName = resolver.getMethodName(exported.binderClass, exported.transactionCode); } else { methodName = previousMethodName; } previousMethodName = methodName; exported.methodName = methodName; + previous = exported; } // Debug entries added to help validate the data. @@ -649,14 +679,32 @@ public class BinderCallsStats implements BinderInternal.Observer { public long maxRequestSizeBytes; public long maxReplySizeBytes; public long exceptionCount; + // Call count since reset + public long incrementalCallCount; - CallStat(int callingUid, Class<? extends Binder> binderClass, int transactionCode, + public CallStat(int callingUid, Class<? extends Binder> binderClass, int transactionCode, boolean screenInteractive) { this.callingUid = callingUid; this.binderClass = binderClass; this.transactionCode = transactionCode; this.screenInteractive = screenInteractive; } + + @Override + public String toString() { + // This is expensive, but CallStat.toString() is only used for debugging. + String methodName = new BinderTransactionNameResolver().getMethodName(binderClass, + transactionCode); + return "CallStat{" + + "callingUid=" + callingUid + + ", transaction=" + binderClass.getSimpleName() + '.' + methodName + + ", callCount=" + callCount + + ", incrementalCallCount=" + incrementalCallCount + + ", recordedCallCount=" + recordedCallCount + + ", cpuTimeMicros=" + cpuTimeMicros + + ", latencyMicros=" + latencyMicros + + '}'; + } } /** Key used to store CallStat object in a Map. */ @@ -704,13 +752,15 @@ public class BinderCallsStats implements BinderInternal.Observer { // Approximate total CPU usage can be computed by // cpuTimeMicros * callCount / recordedCallCount public long cpuTimeMicros; + // Call count that gets reset after delivery to BatteryStats + public long incrementalCallCount; UidEntry(int uid) { this.workSourceUid = uid; } // Aggregate time spent per each call name: call_desc -> cpu_time_micros - private Map<CallStatKey, CallStat> mCallStats = new ArrayMap<>(); + private ArrayMap<CallStatKey, CallStat> mCallStats = new ArrayMap<>(); private CallStatKey mTempKey = new CallStatKey(); @Nullable diff --git a/core/java/com/android/internal/os/BinderInternal.java b/core/java/com/android/internal/os/BinderInternal.java index 95c36ca8429e..feb5aab94adc 100644 --- a/core/java/com/android/internal/os/BinderInternal.java +++ b/core/java/com/android/internal/os/BinderInternal.java @@ -31,6 +31,7 @@ import dalvik.system.VMRuntime; import java.lang.ref.WeakReference; import java.util.ArrayList; +import java.util.Collection; /** * Private and debugging Binder APIs. @@ -134,6 +135,17 @@ public class BinderInternal { } /** + * Allows to track observe incoming binder call stats. + */ + public interface CallStatsObserver { + /** + * Notes incoming binder call stats associated with this work source UID. + */ + void noteCallStats(int workSourceUid, long incrementalCallCount, + Collection<BinderCallsStats.CallStat> callStats); + } + + /** * Add the calling thread to the IPC thread pool. This function does * not return until the current process is exiting. */ diff --git a/core/java/com/android/internal/os/BinderLatencyObserver.java b/core/java/com/android/internal/os/BinderLatencyObserver.java index 59cc66d79bce..4ca59be4877a 100644 --- a/core/java/com/android/internal/os/BinderLatencyObserver.java +++ b/core/java/com/android/internal/os/BinderLatencyObserver.java @@ -16,22 +16,37 @@ package com.android.internal.os; +import static com.android.internal.os.BinderLatencyProto.Dims.SYSTEM_SERVER; + import android.annotation.Nullable; import android.os.Binder; +import android.os.Handler; +import android.os.Looper; import android.os.SystemClock; import android.util.ArrayMap; import android.util.Slog; +import android.util.proto.ProtoOutputStream; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.os.BinderInternal.CallSession; +import com.android.internal.os.BinderLatencyProto.ApiStats; +import com.android.internal.os.BinderLatencyProto.Dims; +import com.android.internal.os.BinderLatencyProto.RepeatedApiStats; +import com.android.internal.util.FrameworkStatsLog; import java.util.Random; /** Collects statistics about Binder call latency per calling API and method. */ public class BinderLatencyObserver { private static final String TAG = "BinderLatencyObserver"; + private static final int MAX_ATOM_SIZE_BYTES = 4064; + // Be conservative and leave 1K space for the last histogram so we don't go over the size limit. + private static final int LAST_HISTOGRAM_BUFFER_SIZE_BYTES = 1000; + + // Latency observer parameters. public static final int PERIODIC_SAMPLING_INTERVAL_DEFAULT = 10; + public static final int STATSD_PUSH_INTERVAL_MINUTES_DEFAULT = 360; // Histogram buckets parameters. public static final int BUCKET_COUNT_DEFAULT = 100; @@ -50,20 +65,124 @@ public class BinderLatencyObserver { private int mFirstBucketSize = FIRST_BUCKET_SIZE_DEFAULT; private float mBucketScaleFactor = BUCKET_SCALE_FACTOR_DEFAULT; + private int mStatsdPushIntervalMinutes = STATSD_PUSH_INTERVAL_MINUTES_DEFAULT; + private final Random mRandom; private BinderLatencyBuckets mLatencyBuckets; + private final Handler mLatencyObserverHandler; + + private Runnable mLatencyObserverRunnable = new Runnable() { + @Override + public void run() { + // Schedule the next push. + noteLatencyDelayed(); + + ArrayMap<LatencyDims, int[]> histogramMap; + synchronized (mLock) { + // Copy the histograms map so we don't use the lock for longer than needed. + histogramMap = new ArrayMap<>(mLatencyHistograms); + mLatencyHistograms.clear(); + } + + BinderTransactionNameResolver resolver = new BinderTransactionNameResolver(); + ProtoOutputStream proto = new ProtoOutputStream(); + int histogramsWritten = 0; + + for (LatencyDims dims : histogramMap.keySet()) { + // Start a new atom if the next histogram risks going over the atom size limit. + if (proto.getRawSize() + LAST_HISTOGRAM_BUFFER_SIZE_BYTES > getMaxAtomSizeBytes()) { + if (histogramsWritten > 0) { + writeAtomToStatsd(proto); + } + proto = new ProtoOutputStream(); + histogramsWritten = 0; + } + + String transactionName = resolver.getMethodName( + dims.getBinderClass(), dims.getTransactionCode()); + fillApiStatsProto(proto, dims, transactionName, histogramMap.get(dims)); + histogramsWritten++; + } + // Push the final atom. + if (histogramsWritten > 0) { + writeAtomToStatsd(proto); + } + } + }; + + private void fillApiStatsProto( + ProtoOutputStream proto, LatencyDims dims, String transactionName, int[] histogram) { + // Find the part of the histogram to write. + int firstNonEmptyBucket = 0; + for (int i = 0; i < mBucketCount; i++) { + if (histogram[i] != 0) { + firstNonEmptyBucket = i; + break; + } + } + int lastNonEmptyBucket = mBucketCount - 1; + for (int i = mBucketCount - 1; i >= 0; i--) { + if (histogram[i] != 0) { + lastNonEmptyBucket = i; + break; + } + } + + // Start a new ApiStats proto. + long apiStatsToken = proto.start(RepeatedApiStats.API_STATS); + + // Write the dims. + long dimsToken = proto.start(ApiStats.DIMS); + proto.write(Dims.PROCESS_SOURCE, SYSTEM_SERVER); + proto.write(Dims.SERVICE_CLASS_NAME, dims.getBinderClass().getName()); + proto.write(Dims.SERVICE_METHOD_NAME, transactionName); + proto.end(dimsToken); + + // Write the histogram. + proto.write(ApiStats.FIRST_BUCKET_INDEX, firstNonEmptyBucket); + for (int i = firstNonEmptyBucket; i <= lastNonEmptyBucket; i++) { + proto.write(ApiStats.BUCKETS, histogram[i]); + } + + proto.end(apiStatsToken); + } + + protected int getMaxAtomSizeBytes() { + return MAX_ATOM_SIZE_BYTES; + } + + protected void writeAtomToStatsd(ProtoOutputStream atom) { + FrameworkStatsLog.write( + FrameworkStatsLog.BINDER_LATENCY_REPORTED, + atom.getBytes(), + mPeriodicSamplingInterval, + 1); + } + + private void noteLatencyDelayed() { + mLatencyObserverHandler.removeCallbacks(mLatencyObserverRunnable); + mLatencyObserverHandler.postDelayed(mLatencyObserverRunnable, + mStatsdPushIntervalMinutes * 60 * 1000); + } + /** Injector for {@link BinderLatencyObserver}. */ public static class Injector { public Random getRandomGenerator() { return new Random(); } + + public Handler getHandler() { + return new Handler(Looper.getMainLooper()); + } } public BinderLatencyObserver(Injector injector) { mRandom = injector.getRandomGenerator(); + mLatencyObserverHandler = injector.getHandler(); mLatencyBuckets = new BinderLatencyBuckets( mBucketCount, mFirstBucketSize, mBucketScaleFactor); + noteLatencyDelayed(); } /** Should be called when a Binder call completes, will store latency data. */ @@ -73,7 +192,8 @@ public class BinderLatencyObserver { } LatencyDims dims = new LatencyDims(s.binderClass, s.transactionCode); - long callDuration = getElapsedRealtimeMicro() - s.timeStarted; + long elapsedTimeMicro = getElapsedRealtimeMicro(); + long callDuration = elapsedTimeMicro - s.timeStarted; // Find the bucket this sample should go to. int bucketIdx = mLatencyBuckets.sampleToBucket( @@ -117,6 +237,22 @@ public class BinderLatencyObserver { } } + /** Updates the statsd push interval. */ + public void setPushInterval(int pushIntervalMinutes) { + if (pushIntervalMinutes <= 0) { + Slog.w(TAG, "Ignored invalid push interval (value must be positive): " + + pushIntervalMinutes); + return; + } + + synchronized (mLock) { + if (pushIntervalMinutes != mStatsdPushIntervalMinutes) { + mStatsdPushIntervalMinutes = pushIntervalMinutes; + reset(); + } + } + } + /** Updates the histogram buckets parameters. */ public void setHistogramBucketsParams( int bucketCount, int firstBucketSize, float bucketScaleFactor) { @@ -138,6 +274,7 @@ public class BinderLatencyObserver { synchronized (mLock) { mLatencyHistograms.clear(); } + noteLatencyDelayed(); } /** Container for binder latency information. */ @@ -187,4 +324,9 @@ public class BinderLatencyObserver { public ArrayMap<LatencyDims, int[]> getLatencyHistograms() { return mLatencyHistograms; } + + @VisibleForTesting + public Runnable getStatsdPushRunnable() { + return mLatencyObserverRunnable; + } } diff --git a/core/java/com/android/internal/os/BinderTransactionNameResolver.java b/core/java/com/android/internal/os/BinderTransactionNameResolver.java new file mode 100644 index 000000000000..5f6f427d344c --- /dev/null +++ b/core/java/com/android/internal/os/BinderTransactionNameResolver.java @@ -0,0 +1,89 @@ +/* + * 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.internal.os; + +import android.os.Binder; + +import com.android.internal.annotations.VisibleForTesting; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.HashMap; + +/** + * Maps a binder class and transaction code to the default transaction name. Since this + * resolution is class-based as opposed to instance-based, any custom implementation of + * {@link Binder#getTransactionName} will be ignored. + * + * The class is NOT thread safe + * + * @hide + */ +@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) +public class BinderTransactionNameResolver { + private static final Method NO_GET_DEFAULT_TRANSACTION_NAME_METHOD; + + /** + * Generates the default transaction method name, which is just the transaction code. + * Used when the binder does not define a static "getDefaultTransactionName" method. + * + * @hide + */ + public static String noDefaultTransactionName(int transactionCode) { + return String.valueOf(transactionCode); + } + + static { + try { + NO_GET_DEFAULT_TRANSACTION_NAME_METHOD = BinderTransactionNameResolver.class.getMethod( + "noDefaultTransactionName", int.class); + } catch (NoSuchMethodException e) { + throw new RuntimeException(e); + } + } + + private final HashMap<Class<? extends Binder>, Method> + mGetDefaultTransactionNameMethods = new HashMap<>(); + + /** + * Given a binder class name and transaction code, returns the corresponding method name. + * + * @hide + */ + public String getMethodName(Class<? extends Binder> binderClass, int transactionCode) { + Method method = mGetDefaultTransactionNameMethods.get(binderClass); + if (method == null) { + try { + method = binderClass.getMethod("getDefaultTransactionName", int.class); + } catch (NoSuchMethodException e) { + method = NO_GET_DEFAULT_TRANSACTION_NAME_METHOD; + } + if (method.getReturnType() != String.class + || !Modifier.isStatic(method.getModifiers())) { + method = NO_GET_DEFAULT_TRANSACTION_NAME_METHOD; + } + mGetDefaultTransactionNameMethods.put(binderClass, method); + } + + try { + return (String) method.invoke(null, transactionCode); + } catch (IllegalAccessException | InvocationTargetException e) { + throw new RuntimeException(e); + } + } +} diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java index 0121b789f39a..cb6008bfa961 100644 --- a/core/java/com/android/internal/os/ZygoteInit.java +++ b/core/java/com/android/internal/os/ZygoteInit.java @@ -586,10 +586,18 @@ public class ZygoteInit { codePaths[0], /*dexMetadata*/ null); - File profileDir = Environment.getDataProfilesDePackageDirectory( + File curProfileDir = Environment.getDataProfilesDePackageDirectory( UserHandle.USER_SYSTEM, systemServerPackageName); - String profilePath = new File(profileDir, systemServerProfileName).getAbsolutePath(); - VMRuntime.registerAppInfo(profilePath, codePaths); + String curProfilePath = new File(curProfileDir, systemServerProfileName).getAbsolutePath(); + File refProfileDir = Environment.getDataProfilesDePackageDirectory( + UserHandle.USER_SYSTEM, systemServerPackageName); + String refProfilePath = new File(refProfileDir, systemServerProfileName).getAbsolutePath(); + VMRuntime.registerAppInfo( + systemServerPackageName, + curProfilePath, + refProfilePath, + codePaths, + VMRuntime.CODE_PATH_TYPE_PRIMARY_APK); } /** diff --git a/core/java/com/android/internal/util/OWNERS b/core/java/com/android/internal/util/OWNERS index a0454510b30b..5b68159888cc 100644 --- a/core/java/com/android/internal/util/OWNERS +++ b/core/java/com/android/internal/util/OWNERS @@ -1,5 +1,6 @@ per-file AsyncChannel* = lorenzo@google.com, satk@google.com, etancohen@google.com per-file MessageUtils*, Protocol*, RingBuffer*, TokenBucket* = jchalard@google.com, lorenzo@google.com, satk@google.com +per-file *Notification* = file:/services/core/java/com/android/server/notification/OWNERS per-file Protocol* = etancohen@google.com, lorenzo@google.com per-file State* = jchalard@google.com, lorenzo@google.com, satk@google.com per-file DataClass* = eugenesusla@google.com
\ No newline at end of file diff --git a/core/java/com/android/server/OWNERS b/core/java/com/android/server/OWNERS index 1262925447b9..554e27890476 100644 --- a/core/java/com/android/server/OWNERS +++ b/core/java/com/android/server/OWNERS @@ -1 +1 @@ -per-file SystemConfig.java = toddke@google.com +per-file SystemConfig.java = toddke@google.com,patb@google.com diff --git a/core/jni/com_android_internal_net_NetworkUtilsInternal.cpp b/core/jni/com_android_internal_net_NetworkUtilsInternal.cpp index 980e12d0bb40..83e2f2b3f89e 100644 --- a/core/jni/com_android_internal_net_NetworkUtilsInternal.cpp +++ b/core/jni/com_android_internal_net_NetworkUtilsInternal.cpp @@ -31,7 +31,7 @@ static jboolean android_net_utils_protectFromVpn(JNIEnv *env, jobject thiz, jint } static jboolean android_net_utils_protectFromVpnWithFd(JNIEnv *env, jobject thiz, jobject javaFd) { - return android_net_utils_protectFromVpn(env, thiz, AFileDescriptor_getFD(env, javaFd)); + return android_net_utils_protectFromVpn(env, thiz, AFileDescriptor_getFd(env, javaFd)); } static const JNINativeMethod gNetworkUtilMethods[] = { diff --git a/core/proto/OWNERS b/core/proto/OWNERS index e62b5c102a59..44ea23fdf685 100644 --- a/core/proto/OWNERS +++ b/core/proto/OWNERS @@ -14,9 +14,10 @@ per-file settings_enums.proto=tmfang@google.com # Frameworks ogunwale@google.com jjaggi@google.com +kwekua@google.com roosa@google.com -per-file package_item_info.proto = toddke@google.com -per-file usagestatsservice.proto, usagestatsservice_v2.proto = mwachens@google.com +per-file package_item_info.proto = toddke@google.com,patb@google.com +per-file usagestatsservice.proto, usagestatsservice_v2.proto = file:/core/java/android/app/usage/OWNERS per-file apphibernationservice.proto = file:/core/java/android/apphibernation/OWNERS # Biometrics diff --git a/core/proto/android/internal/binder_latency.proto b/core/proto/android/internal/binder_latency.proto new file mode 100644 index 000000000000..e32c3e3441c5 --- /dev/null +++ b/core/proto/android/internal/binder_latency.proto @@ -0,0 +1,77 @@ +/* + * 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. + */ + +syntax = "proto2"; +package com.android.internal.os; + +option java_outer_classname = "BinderLatencyProto"; + +/** + * RepeatedApiStats proto from atoms.proto, duplicated here so that it's + * accessible in the build. + * Must be kept in sync with the version in atoms.proto. + */ + +message RepeatedApiStats { + repeated ApiStats api_stats = 1; +} + +message Dims { + enum ProcessSource { + UNKNOWN_PROCESS_SOURCE = 0; + SYSTEM_SERVER = 1; + TELEPHONY = 2; + } + + enum ServiceClassName { + UNKNOWN_CLASS = 0; + } + enum ServiceMethodName { + UNKNOWN_METHOD = 0; + } + + // Required. + optional ProcessSource process_source = 1; + + // The class name of the API making the call to Binder. Enum value + // is preferred as uses much less data to store. + // This field does not contain PII. + oneof service_class { + ServiceClassName service_class_name_as_enum = 2; + string service_class_name = 3; + } + + // Method name of the API call. It can also be a transaction code if we + // cannot resolve it to a name. See Binder#getTransactionName. Enum value + // is preferred as uses much less data to store. + // This field does not contain PII. + oneof service_method { + ServiceMethodName service_method_name_as_enum = 4; + string service_method_name = 5; + } +} + +message ApiStats { + // required. + optional Dims dims = 1; + + // Indicates the first bucket that had any data. Allows omitting any empty + // buckets at the start of the bucket list and thus save on data size. + optional int32 first_bucket_index = 2; + // Stores the count of samples for each bucket. The number of buckets and + // their sizes are controlled server side with a flag. + repeated int32 buckets = 3; +}
\ No newline at end of file diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsBinderCallStatsTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsBinderCallStatsTest.java new file mode 100644 index 000000000000..3e67b8bffa63 --- /dev/null +++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsBinderCallStatsTest.java @@ -0,0 +1,93 @@ +/* + * 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.internal.os; + +import android.os.Binder; +import android.os.Process; +import android.util.ArraySet; + +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import junit.framework.TestCase; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.ArrayList; +import java.util.Collection; + +/** + * Test cases for android.os.BatteryStats, system server Binder call stats. + */ +@RunWith(AndroidJUnit4.class) +@SmallTest +public class BatteryStatsBinderCallStatsTest extends TestCase { + + private static final int TRANSACTION_CODE = 100; + + /** + * Test BatteryStatsImpl.Uid.noteBinderCallStats. + */ + @Test + public void testNoteBinderCallStats() throws Exception { + final MockClocks clocks = new MockClocks(); // holds realtime and uptime in ms + MockBatteryStatsImpl bi = new MockBatteryStatsImpl(clocks); + + int callingUid = Process.FIRST_APPLICATION_UID + 1; + int workSourceUid = Process.FIRST_APPLICATION_UID + 1; + + Collection<BinderCallsStats.CallStat> callStats = new ArrayList<>(); + BinderCallsStats.CallStat stat1 = new BinderCallsStats.CallStat(callingUid, + MockBinder.class, TRANSACTION_CODE, true /*screenInteractive */); + stat1.incrementalCallCount = 21; + stat1.recordedCallCount = 5; + stat1.cpuTimeMicros = 1000; + callStats.add(stat1); + + bi.noteBinderCallStats(workSourceUid, 42, callStats); + + callStats.clear(); + BinderCallsStats.CallStat stat2 = new BinderCallsStats.CallStat(callingUid, + MockBinder.class, TRANSACTION_CODE, true /*screenInteractive */); + stat2.incrementalCallCount = 9; + stat2.recordedCallCount = 8; + stat2.cpuTimeMicros = 500; + callStats.add(stat2); + + bi.noteBinderCallStats(workSourceUid, 8, callStats); + + BatteryStatsImpl.Uid uid = bi.getUidStatsLocked(workSourceUid); + assertEquals(42 + 8, uid.getBinderCallCount()); + + BinderTransactionNameResolver resolver = new BinderTransactionNameResolver(); + ArraySet<BatteryStatsImpl.BinderCallStats> stats = uid.getBinderCallStats(); + assertEquals(1, stats.size()); + BatteryStatsImpl.BinderCallStats value = stats.valueAt(0); + value.ensureMethodName(resolver); + assertEquals("testMethod", value.getMethodName()); + assertEquals(21 + 9, value.callCount); + assertEquals(8, value.recordedCallCount); + assertEquals(500, value.recordedCpuTimeMicros); + } + + private static class MockBinder extends Binder { + public static String getDefaultTransactionName(int txCode) { + return txCode == TRANSACTION_CODE ? "testMethod" : "unknown"; + } + } +} diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java index 2ad8e18741f9..7807f019914e 100644 --- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java +++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java @@ -23,6 +23,7 @@ import org.junit.runners.Suite; @Suite.SuiteClasses({ BatteryStatsCpuTimesTest.class, BatteryStatsBackgroundStatsTest.class, + BatteryStatsBinderCallStatsTest.class, BatteryStatsCounterTest.class, BatteryStatsDualTimerTest.class, BatteryStatsDurationTimerTest.class, diff --git a/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java b/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java index cec62164e57a..68c68704a1b3 100644 --- a/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java +++ b/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java @@ -16,10 +16,16 @@ package com.android.internal.os; +import static com.google.common.truth.Truth.assertThat; + import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import android.os.Binder; +import android.os.Handler; +import android.os.Looper; +import android.os.Message; +import android.os.Process; import android.os.SystemClock; import android.platform.test.annotations.Presubmit; import android.util.ArrayMap; @@ -46,11 +52,12 @@ import java.util.Random; @RunWith(AndroidJUnit4.class) @Presubmit public class BinderCallsStatsTest { - private static final int WORKSOURCE_UID = 1; + private static final int WORKSOURCE_UID = Process.FIRST_APPLICATION_UID; private static final int CALLING_UID = 2; private static final int REQUEST_SIZE = 2; private static final int REPLY_SIZE = 3; private final CachedDeviceState mDeviceState = new CachedDeviceState(false, true); + private final TestHandler mHandler = new TestHandler(); @Test public void testDetailedOff() { @@ -754,6 +761,53 @@ public class BinderCallsStatsTest { } @Test + public void testCallStatsObserver() { + TestBinderCallsStats bcs = new TestBinderCallsStats(); + bcs.setSamplingInterval(1); + bcs.setTrackScreenInteractive(false); + + final ArrayList<BinderCallsStats.CallStat> callStatsList = new ArrayList<>(); + bcs.setCallStatsObserver( + (workSourceUid, incrementalCallCount, callStats) -> callStatsList.addAll( + callStats)); + + Binder binder = new Binder(); + + CallSession callSession = bcs.callStarted(binder, 1, WORKSOURCE_UID); + bcs.time += 10; + bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE, WORKSOURCE_UID); + + callSession = bcs.callStarted(binder, 1, WORKSOURCE_UID); + bcs.time += 20; + bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE, WORKSOURCE_UID); + + callSession = bcs.callStarted(binder, 2, WORKSOURCE_UID); + bcs.time += 30; + bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE, WORKSOURCE_UID); + + for (Runnable runnable: mHandler.mRunnables) { + // Execute all pending runnables. Ignore the delay. + runnable.run(); + } + + assertThat(callStatsList).hasSize(2); + for (int i = 0; i < 2; i++) { + BinderCallsStats.CallStat callStats = callStatsList.get(i); + if (callStats.transactionCode == 1) { + assertEquals(2, callStats.callCount); + assertEquals(2, callStats.recordedCallCount); + assertEquals(30, callStats.cpuTimeMicros); + assertEquals(20, callStats.maxCpuTimeMicros); + } else { + assertEquals(1, callStats.callCount); + assertEquals(1, callStats.recordedCallCount); + assertEquals(30, callStats.cpuTimeMicros); + assertEquals(30, callStats.maxCpuTimeMicros); + } + } + } + + @Test public void testLatencyCollectionEnabled() { TestBinderCallsStats bcs = new TestBinderCallsStats(); bcs.setCollectLatencyData(true); @@ -781,6 +835,20 @@ public class BinderCallsStatsTest { assertEquals(0, bcs.getLatencyObserver().getLatencyHistograms().size()); } + private static class TestHandler extends Handler { + ArrayList<Runnable> mRunnables = new ArrayList<>(); + + TestHandler() { + super(Looper.getMainLooper()); + } + + @Override + public boolean sendMessageAtTime(Message msg, long uptimeMillis) { + mRunnables.add(msg.getCallback()); + return true; + } + } + class TestBinderCallsStats extends BinderCallsStats { public int callingUid = CALLING_UID; public long time = 1234; @@ -803,6 +871,10 @@ public class BinderCallsStatsTest { }; } + public Handler getHandler() { + return mHandler; + } + public BinderLatencyObserver getLatencyObserver() { return new BinderLatencyObserverTest.TestBinderLatencyObserver(); } diff --git a/core/tests/coretests/src/com/android/internal/os/BinderLatencyObserverTest.java b/core/tests/coretests/src/com/android/internal/os/BinderLatencyObserverTest.java index f65fb9583d6c..bf87683593e7 100644 --- a/core/tests/coretests/src/com/android/internal/os/BinderLatencyObserverTest.java +++ b/core/tests/coretests/src/com/android/internal/os/BinderLatencyObserverTest.java @@ -16,6 +16,8 @@ package com.android.internal.os; +import static com.android.internal.os.BinderLatencyProto.Dims.SYSTEM_SERVER; + import static com.google.common.truth.Truth.assertThat; import static org.junit.Assert.assertEquals; @@ -23,16 +25,21 @@ import static org.junit.Assert.assertEquals; import android.os.Binder; import android.platform.test.annotations.Presubmit; import android.util.ArrayMap; +import android.util.proto.ProtoOutputStream; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; import com.android.internal.os.BinderInternal.CallSession; import com.android.internal.os.BinderLatencyObserver.LatencyDims; +import com.android.internal.os.BinderLatencyProto.ApiStats; +import com.android.internal.os.BinderLatencyProto.Dims; +import com.android.internal.os.BinderLatencyProto.RepeatedApiStats; import org.junit.Test; import org.junit.runner.RunWith; +import java.util.ArrayList; import java.util.Arrays; import java.util.Random; @@ -49,11 +56,17 @@ public class BinderLatencyObserverTest { CallSession callSession = new CallSession(); callSession.binderClass = binder.getClass(); callSession.transactionCode = 1; + + blo.setElapsedTime(2); blo.callEnded(callSession); + blo.setElapsedTime(4); blo.callEnded(callSession); + blo.setElapsedTime(6); blo.callEnded(callSession); callSession.transactionCode = 2; + blo.setElapsedTime(8); blo.callEnded(callSession); + blo.setElapsedTime(10); blo.callEnded(callSession); ArrayMap<LatencyDims, int[]> latencyHistograms = blo.getLatencyHistograms(); @@ -74,8 +87,10 @@ public class BinderLatencyObserverTest { CallSession callSession = new CallSession(); callSession.binderClass = binder.getClass(); callSession.transactionCode = 1; + blo.setElapsedTime(2); blo.callEnded(callSession); callSession.transactionCode = 2; + blo.setElapsedTime(4); blo.callEnded(callSession); ArrayMap<LatencyDims, int[]> latencyHistograms = blo.getLatencyHistograms(); @@ -89,13 +104,13 @@ public class BinderLatencyObserverTest { @Test public void testTooCallLengthOverflow() { TestBinderLatencyObserver blo = new TestBinderLatencyObserver(); - blo.setElapsedTime(2L + (long) Integer.MAX_VALUE); blo.setHistogramBucketsParams(5, 5, 1.125f); Binder binder = new Binder(); CallSession callSession = new CallSession(); callSession.binderClass = binder.getClass(); callSession.transactionCode = 1; + blo.setElapsedTime(2L + (long) Integer.MAX_VALUE); blo.callEnded(callSession); // The long call should be capped to maxint (to not overflow) and placed in the last bucket. @@ -114,6 +129,7 @@ public class BinderLatencyObserverTest { CallSession callSession = new CallSession(); callSession.binderClass = binder.getClass(); callSession.transactionCode = 1; + blo.setElapsedTime(2); blo.callEnded(callSession); LatencyDims dims = new LatencyDims(binder.getClass(), 1); @@ -122,14 +138,111 @@ public class BinderLatencyObserverTest { assertThat(blo.getLatencyHistograms().get(dims)) .asList().containsExactly(Integer.MAX_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE); // Try to add another sample. + blo.setElapsedTime(2); blo.callEnded(callSession); // Make sure the buckets don't overflow. assertThat(blo.getLatencyHistograms().get(dims)) .asList().containsExactly(Integer.MAX_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE); } + @Test + public void testSingleAtomPush() { + TestBinderLatencyObserver blo = new TestBinderLatencyObserver(); + + Binder binder = new Binder(); + CallSession callSession = new CallSession(); + callSession.binderClass = binder.getClass(); + callSession.transactionCode = 1; + blo.setElapsedTime(7); + blo.callEnded(callSession); + blo.callEnded(callSession); + blo.setElapsedTime(8); + blo.callEnded(callSession); + + // Trigger the statsd push. + blo.getStatsdPushRunnable().run(); + + ProtoOutputStream expectedProto = new ProtoOutputStream(); + long apiStatsToken = expectedProto.start(RepeatedApiStats.API_STATS); + long dimsToken = expectedProto.start(ApiStats.DIMS); + expectedProto.write(Dims.PROCESS_SOURCE, SYSTEM_SERVER); + expectedProto.write(Dims.SERVICE_CLASS_NAME, binder.getClass().getName()); + expectedProto.write(Dims.SERVICE_METHOD_NAME, "1"); + expectedProto.end(dimsToken); + expectedProto.write(ApiStats.FIRST_BUCKET_INDEX, 3); + expectedProto.write(ApiStats.BUCKETS, 2); + expectedProto.write(ApiStats.BUCKETS, 1); + expectedProto.end(apiStatsToken); + + assertThat(blo.getWrittenAtoms()) + .containsExactly(Arrays.toString(expectedProto.getBytes())); + } + + @Test + public void testMultipleAtomPush() { + TestBinderLatencyObserver blo = new TestBinderLatencyObserver(); + + BinderTransactionNameResolver resolver = new BinderTransactionNameResolver(); + + + Binder binder = new Binder(); + CallSession callSession = new CallSession(); + callSession.binderClass = binder.getClass(); + callSession.transactionCode = 1; + blo.setElapsedTime(1); + blo.callEnded(callSession); + callSession.transactionCode = 2; + blo.setElapsedTime(5); + blo.callEnded(callSession); + callSession.transactionCode = 3; + blo.callEnded(callSession); + + // Trigger the statsd push. + blo.getStatsdPushRunnable().run(); + + ProtoOutputStream expectedProto1 = new ProtoOutputStream(); + long apiStatsToken = expectedProto1.start(RepeatedApiStats.API_STATS); + long dimsToken = expectedProto1.start(ApiStats.DIMS); + expectedProto1.write(Dims.PROCESS_SOURCE, SYSTEM_SERVER); + expectedProto1.write(Dims.SERVICE_CLASS_NAME, binder.getClass().getName()); + expectedProto1.write(Dims.SERVICE_METHOD_NAME, "1"); + expectedProto1.end(dimsToken); + expectedProto1.write(ApiStats.FIRST_BUCKET_INDEX, 0); + expectedProto1.write(ApiStats.BUCKETS, 1); + expectedProto1.end(apiStatsToken); + + apiStatsToken = expectedProto1.start(RepeatedApiStats.API_STATS); + dimsToken = expectedProto1.start(ApiStats.DIMS); + expectedProto1.write(Dims.PROCESS_SOURCE, SYSTEM_SERVER); + expectedProto1.write(Dims.SERVICE_CLASS_NAME, binder.getClass().getName()); + expectedProto1.write(Dims.SERVICE_METHOD_NAME, "2"); + expectedProto1.end(dimsToken); + expectedProto1.write(ApiStats.FIRST_BUCKET_INDEX, 1); + expectedProto1.write(ApiStats.BUCKETS, 1); + expectedProto1.end(apiStatsToken); + + ProtoOutputStream expectedProto2 = new ProtoOutputStream(); + apiStatsToken = expectedProto2.start(RepeatedApiStats.API_STATS); + dimsToken = expectedProto2.start(ApiStats.DIMS); + expectedProto2.write(Dims.PROCESS_SOURCE, SYSTEM_SERVER); + expectedProto2.write(Dims.SERVICE_CLASS_NAME, binder.getClass().getName()); + expectedProto2.write(Dims.SERVICE_METHOD_NAME, "3"); + expectedProto2.end(dimsToken); + expectedProto2.write(ApiStats.FIRST_BUCKET_INDEX, 1); + expectedProto2.write(ApiStats.BUCKETS, 1); + expectedProto2.end(apiStatsToken); + + // Each ApiStats is around ~60 bytes so only two should fit in an atom. + assertThat(blo.getWrittenAtoms()) + .containsExactly( + Arrays.toString(expectedProto1.getBytes()), + Arrays.toString(expectedProto2.getBytes())) + .inOrder(); + } + public static class TestBinderLatencyObserver extends BinderLatencyObserver { private long mElapsedTime = 0; + private ArrayList<String> mWrittenAtoms; TestBinderLatencyObserver() { // Make random generator not random. @@ -145,16 +258,30 @@ public class BinderLatencyObserverTest { } }); setSamplingInterval(1); + mWrittenAtoms = new ArrayList<>(); } @Override protected long getElapsedRealtimeMicro() { - mElapsedTime += 2; return mElapsedTime; } + @Override + protected int getMaxAtomSizeBytes() { + return 1100; + } + + @Override + protected void writeAtomToStatsd(ProtoOutputStream atom) { + mWrittenAtoms.add(Arrays.toString(atom.getBytes())); + } + public void setElapsedTime(long time) { mElapsedTime = time; } + + public ArrayList<String> getWrittenAtoms() { + return mWrittenAtoms; + } } } diff --git a/core/tests/utiltests/src/com/android/internal/util/FileRotatorTest.java b/core/tests/utiltests/src/com/android/internal/util/FileRotatorTest.java index 3f9e62e7b180..952721320c90 100644 --- a/core/tests/utiltests/src/com/android/internal/util/FileRotatorTest.java +++ b/core/tests/utiltests/src/com/android/internal/util/FileRotatorTest.java @@ -29,6 +29,8 @@ import android.util.Log; import com.android.internal.util.FileRotator.Reader; import com.android.internal.util.FileRotator.Writer; +import com.android.internal.util.test.FsUtil; + import com.google.android.collect.Lists; import java.io.DataInputStream; @@ -38,15 +40,10 @@ import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; -import java.net.ProtocolException; import java.util.ArrayList; import java.util.Arrays; import java.util.Random; -import junit.framework.Assert; - -import libcore.io.IoUtils; - /** * Tests for {@link FileRotator}. */ @@ -67,7 +64,7 @@ public class FileRotatorTest extends AndroidTestCase { super.setUp(); mBasePath = getContext().getFilesDir(); - IoUtils.deleteContents(mBasePath); + FsUtil.deleteContents(mBasePath); } public void testEmpty() throws Exception { diff --git a/data/etc/OWNERS b/data/etc/OWNERS index 5aacfddab28c..ea23aba16d12 100644 --- a/data/etc/OWNERS +++ b/data/etc/OWNERS @@ -10,6 +10,7 @@ svetoslavganov@android.com svetoslavganov@google.com toddke@android.com toddke@google.com +patb@google.com yamasani@google.com per-file preinstalled-packages* = file:/MULTIUSER_OWNERS diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml index 62e11b9baca2..ee2387b167a2 100644 --- a/data/etc/privapp-permissions-platform.xml +++ b/data/etc/privapp-permissions-platform.xml @@ -444,6 +444,7 @@ applications that come with the platform <permission name="android.permission.MANAGE_DEBUGGING" /> <!-- Permissions required for CTS test - TimeManagerTest --> <permission name="android.permission.MANAGE_TIME_AND_ZONE_DETECTION" /> + <permission name="android.permission.SUGGEST_EXTERNAL_TIME" /> <!-- Permissions required for CTS test - CtsHdmiCecHostTestCases --> <permission name="android.permission.HDMI_CEC"/> <!-- Permission required for CTS test - MediaPlayerTest --> diff --git a/data/keyboards/Vendor_0171_Product_0419.kl b/data/keyboards/Vendor_0171_Product_0419.kl new file mode 100644 index 000000000000..05a25f0210c8 --- /dev/null +++ b/data/keyboards/Vendor_0171_Product_0419.kl @@ -0,0 +1,55 @@ +# 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. + +# +# Amazon Luna Controller +# + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y +key 310 BUTTON_L1 +key 311 BUTTON_R1 + +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +# Left and right stick. +axis 0x00 X flat 4096 +axis 0x01 Y flat 4096 +axis 0x02 Z flat 4096 +axis 0x05 RZ flat 4096 + +# Triggers. +axis 0x0a LTRIGGER +axis 0x09 RTRIGGER + +# Hat. +axis 0x10 HAT_X +axis 0x11 HAT_Y + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt + +# Action button (circle icon, left of the Home button) +key 158 BUTTON_SELECT + +# Home button (branded button in the center of the controller) +key 172 BUTTON_MODE + +# Menu button (hamburger icon, right of the Home button) +key 315 BUTTON_START + +# Alexa Push-To-Talk button (microphone icon, below the Home button) +key 217 MEDIA_RECORD diff --git a/libs/androidfw/OWNERS b/libs/androidfw/OWNERS index bc056df23a36..610fd80fe73c 100644 --- a/libs/androidfw/OWNERS +++ b/libs/androidfw/OWNERS @@ -1,6 +1,7 @@ set noparent toddke@google.com rtmitchell@google.com +patb@google.com per-file CursorWindow.cpp=omakoto@google.com per-file LocaleDataTables.cpp=vichang@google.com,ngeoffray@google.com,nikitai@google.com diff --git a/packages/Connectivity/framework/jni/android_net_NetworkUtils.cpp b/packages/Connectivity/framework/jni/android_net_NetworkUtils.cpp index 48e262a6b19b..f17baf9f6733 100644 --- a/packages/Connectivity/framework/jni/android_net_NetworkUtils.cpp +++ b/packages/Connectivity/framework/jni/android_net_NetworkUtils.cpp @@ -76,7 +76,7 @@ static void android_net_utils_attachDropAllBPFFilter(JNIEnv *env, jobject clazz, filter_code, }; - int fd = AFileDescriptor_getFD(env, javaFd); + int fd = AFileDescriptor_getFd(env, javaFd); if (setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &filter, sizeof(filter)) != 0) { jniThrowExceptionFmt(env, "java/net/SocketException", "setsockopt(SO_ATTACH_FILTER): %s", strerror(errno)); @@ -86,7 +86,7 @@ static void android_net_utils_attachDropAllBPFFilter(JNIEnv *env, jobject clazz, static void android_net_utils_detachBPFFilter(JNIEnv *env, jobject clazz, jobject javaFd) { int optval_ignored = 0; - int fd = AFileDescriptor_getFD(env, javaFd); + int fd = AFileDescriptor_getFd(env, javaFd); if (setsockopt(fd, SOL_SOCKET, SO_DETACH_FILTER, &optval_ignored, sizeof(optval_ignored)) != 0) { jniThrowExceptionFmt(env, "java/net/SocketException", @@ -112,7 +112,7 @@ static jboolean android_net_utils_bindProcessToNetworkForHostResolution(JNIEnv * static jint android_net_utils_bindSocketToNetwork(JNIEnv *env, jobject thiz, jobject javaFd, jint netId) { - return setNetworkForSocket(netId, AFileDescriptor_getFD(env, javaFd)); + return setNetworkForSocket(netId, AFileDescriptor_getFd(env, javaFd)); } static bool checkLenAndCopy(JNIEnv* env, const jbyteArray& addr, int len, void* dst) @@ -160,7 +160,7 @@ static jobject android_net_utils_resNetworkSend(JNIEnv *env, jobject thiz, jint } static jobject android_net_utils_resNetworkResult(JNIEnv *env, jobject thiz, jobject javaFd) { - int fd = AFileDescriptor_getFD(env, javaFd); + int fd = AFileDescriptor_getFd(env, javaFd); int rcode; std::vector<uint8_t> buf(MAXPACKETSIZE, 0); @@ -187,7 +187,7 @@ static jobject android_net_utils_resNetworkResult(JNIEnv *env, jobject thiz, job } static void android_net_utils_resNetworkCancel(JNIEnv *env, jobject thiz, jobject javaFd) { - int fd = AFileDescriptor_getFD(env, javaFd); + int fd = AFileDescriptor_getFd(env, javaFd); resNetworkCancel(fd); jniSetFileDescriptorOfFD(env, javaFd, -1); } @@ -213,7 +213,7 @@ static jobject android_net_utils_getTcpRepairWindow(JNIEnv *env, jobject thiz, j return NULL; } - int fd = AFileDescriptor_getFD(env, javaFd); + int fd = AFileDescriptor_getFd(env, javaFd); struct tcp_repair_window trw = {}; socklen_t size = sizeof(trw); diff --git a/packages/Connectivity/framework/src/android/net/ConnectivityDiagnosticsManager.java b/packages/Connectivity/framework/src/android/net/ConnectivityDiagnosticsManager.java index 3598ebc70118..dcc8a5eacd13 100644 --- a/packages/Connectivity/framework/src/android/net/ConnectivityDiagnosticsManager.java +++ b/packages/Connectivity/framework/src/android/net/ConnectivityDiagnosticsManager.java @@ -713,7 +713,9 @@ public class ConnectivityDiagnosticsManager { * <p>Callbacks registered by apps not meeting the above criteria will not be invoked. * * <p>If a registering app loses its relevant permissions, any callbacks it registered will - * silently stop receiving callbacks. + * silently stop receiving callbacks. Note that registering apps must also have location + * permissions to receive callbacks as some Networks may be location-bound (such as WiFi + * networks). * * <p>Each register() call <b>MUST</b> use a ConnectivityDiagnosticsCallback instance that is * not currently registered. If a ConnectivityDiagnosticsCallback instance is registered with diff --git a/packages/Connectivity/framework/src/android/net/NetworkRequest.java b/packages/Connectivity/framework/src/android/net/NetworkRequest.java index dd88c5a5c94e..e6a96ef74869 100644 --- a/packages/Connectivity/framework/src/android/net/NetworkRequest.java +++ b/packages/Connectivity/framework/src/android/net/NetworkRequest.java @@ -200,8 +200,9 @@ public class NetworkRequest implements Parcelable { private final NetworkCapabilities mNetworkCapabilities; - // A boolean that represents the user modified NOT_VCN_MANAGED capability. - private boolean mModifiedNotVcnManaged = false; + // A boolean that represents whether the NOT_VCN_MANAGED capability should be deduced when + // the NetworkRequest object is built. + private boolean mShouldDeduceNotVcnManaged = true; /** * Default constructor for Builder. @@ -223,7 +224,7 @@ public class NetworkRequest implements Parcelable { // If the caller constructed the builder from a request, it means the user // might explicitly want the capabilities from the request. Thus, the NOT_VCN_MANAGED // capabilities should not be touched later. - mModifiedNotVcnManaged = true; + mShouldDeduceNotVcnManaged = false; } /** @@ -254,7 +255,7 @@ public class NetworkRequest implements Parcelable { public Builder addCapability(@NetworkCapabilities.NetCapability int capability) { mNetworkCapabilities.addCapability(capability); if (capability == NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED) { - mModifiedNotVcnManaged = true; + mShouldDeduceNotVcnManaged = false; } return this; } @@ -268,7 +269,7 @@ public class NetworkRequest implements Parcelable { public Builder removeCapability(@NetworkCapabilities.NetCapability int capability) { mNetworkCapabilities.removeCapability(capability); if (capability == NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED) { - mModifiedNotVcnManaged = true; + mShouldDeduceNotVcnManaged = false; } return this; } @@ -352,7 +353,7 @@ public class NetworkRequest implements Parcelable { mNetworkCapabilities.clearAll(); // If the caller explicitly clear all capabilities, the NOT_VCN_MANAGED capabilities // should not be add back later. - mModifiedNotVcnManaged = true; + mShouldDeduceNotVcnManaged = false; return this; } @@ -453,6 +454,9 @@ public class NetworkRequest implements Parcelable { throw new IllegalArgumentException("A MatchAllNetworkSpecifier is not permitted"); } mNetworkCapabilities.setNetworkSpecifier(networkSpecifier); + // Do not touch NOT_VCN_MANAGED if the caller needs to access to a very specific + // Network. + mShouldDeduceNotVcnManaged = false; return this; } @@ -486,12 +490,13 @@ public class NetworkRequest implements Parcelable { * {@link #VCN_SUPPORTED_CAPABILITIES}, add the NET_CAPABILITY_NOT_VCN_MANAGED to * allow the callers automatically utilize VCN networks if available. * 2. For the requests that explicitly add or remove NET_CAPABILITY_NOT_VCN_MANAGED, + * or has clear intention of tracking specific network, * do not alter them to allow user fire request that suits their need. * * @hide */ private void deduceNotVcnManagedCapability(final NetworkCapabilities nc) { - if (mModifiedNotVcnManaged) return; + if (!mShouldDeduceNotVcnManaged) return; for (final int cap : nc.getCapabilities()) { if (!VCN_SUPPORTED_CAPABILITIES.contains(cap)) return; } diff --git a/packages/Connectivity/framework/src/android/net/apf/ApfCapabilities.java b/packages/Connectivity/framework/src/android/net/apf/ApfCapabilities.java index 85b24713f256..663c1b3d2dc9 100644 --- a/packages/Connectivity/framework/src/android/net/apf/ApfCapabilities.java +++ b/packages/Connectivity/framework/src/android/net/apf/ApfCapabilities.java @@ -131,43 +131,21 @@ public final class ApfCapabilities implements Parcelable { * @return Whether the APF Filter in the device should filter out IEEE 802.3 Frames. */ public static boolean getApfDrop8023Frames() { - // TODO(b/183076074): remove reading resources from system resources + // TODO: deprecate/remove this method (now unused in the platform), as the resource was + // moved to NetworkStack. final Resources systemRes = Resources.getSystem(); final int id = systemRes.getIdentifier("config_apfDrop802_3Frames", "bool", "android"); return systemRes.getBoolean(id); } /** - * @return Whether the APF Filter in the device should filter out IEEE 802.3 Frames. - * @hide - */ - public static boolean getApfDrop8023Frames(@NonNull Context context) { - final ConnectivityResources res = getResources(context); - // TODO(b/183076074): use R.bool.config_apfDrop802_3Frames directly - final int id = res.get().getIdentifier("config_apfDrop802_3Frames", "bool", - res.getResourcesContext().getPackageName()); - return res.get().getBoolean(id); - } - - /** * @return An array of denylisted EtherType, packets with EtherTypes within it will be dropped. */ public static @NonNull int[] getApfEtherTypeBlackList() { - // TODO(b/183076074): remove reading resources from system resources + // TODO: deprecate/remove this method (now unused in the platform), as the resource was + // moved to NetworkStack. final Resources systemRes = Resources.getSystem(); final int id = systemRes.getIdentifier("config_apfEthTypeBlackList", "array", "android"); return systemRes.getIntArray(id); } - - /** - * @return An array of denylisted EtherType, packets with EtherTypes within it will be dropped. - * @hide - */ - public static @NonNull int[] getApfEtherTypeDenyList(@NonNull Context context) { - final ConnectivityResources res = getResources(context); - // TODO(b/183076074): use R.array.config_apfEthTypeDenyList directly - final int id = res.get().getIdentifier("config_apfEthTypeDenyList", "array", - res.getResourcesContext().getPackageName()); - return res.get().getIntArray(id); - } } diff --git a/packages/Connectivity/service/ServiceConnectivityResources/res/values/config.xml b/packages/Connectivity/service/ServiceConnectivityResources/res/values/config.xml index 9ff2a2209e76..078a9eb58202 100644 --- a/packages/Connectivity/service/ServiceConnectivityResources/res/values/config.xml +++ b/packages/Connectivity/service/ServiceConnectivityResources/res/values/config.xml @@ -52,22 +52,6 @@ <item>12,60000</item><!-- mobile_cbs --> </string-array> - <!-- Whether the APF Filter in the device should filter out IEEE 802.3 Frames - Those frames are identified by the field Eth-type having values - less than 0x600 --> - <bool translatable="false" name="config_apfDrop802_3Frames">true</bool> - - <!-- An array of Denylisted EtherType, packets with EtherTypes within this array - will be dropped - TODO: need to put proper values, these are for testing purposes only --> - <integer-array translatable="false" name="config_apfEthTypeDenyList"> - <item>0x88A2</item> - <item>0x88A4</item> - <item>0x88B8</item> - <item>0x88CD</item> - <item>0x88E3</item> - </integer-array> - <!-- Default supported concurrent socket keepalive slots per transport type, used by ConnectivityManager.createSocketKeepalive() for calculating the number of keepalive offload slots that should be reserved for privileged access. This string array should be diff --git a/packages/Connectivity/service/ServiceConnectivityResources/res/values/overlayable.xml b/packages/Connectivity/service/ServiceConnectivityResources/res/values/overlayable.xml index 717d08e13e44..f0f4ae8022e0 100644 --- a/packages/Connectivity/service/ServiceConnectivityResources/res/values/overlayable.xml +++ b/packages/Connectivity/service/ServiceConnectivityResources/res/values/overlayable.xml @@ -21,8 +21,6 @@ <item type="string" name="config_networkCaptivePortalServerUrl"/> <item type="integer" name="config_networkTransitionTimeout"/> <item type="array" name="config_wakeonlan_supported_interfaces"/> - <item type="bool" name="config_apfDrop802_3Frames"/> - <item type="array" name="config_apfEthTypeDenyList"/> <item type="integer" name="config_networkMeteredMultipathPreference"/> <item type="array" name="config_networkSupportedKeepaliveCount"/> <item type="integer" name="config_networkAvoidBadWifi"/> diff --git a/packages/Connectivity/service/src/com/android/server/ConnectivityService.java b/packages/Connectivity/service/src/com/android/server/ConnectivityService.java index 085943a77b5c..842ad6222e40 100644 --- a/packages/Connectivity/service/src/com/android/server/ConnectivityService.java +++ b/packages/Connectivity/service/src/com/android/server/ConnectivityService.java @@ -1045,14 +1045,10 @@ public class ConnectivityService extends IConnectivityManager.Stub } else { // ConnectivityService publishes binder service using publishBinderService() with // no priority assigned will be treated as NORMAL priority. Dumpsys does not send - // "--dump-priority" arguments to the service. Thus, dump both NORMAL and HIGH to - // align the legacy design. + // "--dump-priority" arguments to the service. Thus, dump NORMAL only to align the + // legacy output for dumpsys connectivity. // TODO: Integrate into signal dump. dumpNormal(fd, pw, args); - pw.println(); - pw.println("DUMP OF SERVICE HIGH connectivity"); - pw.println(); - dumpHigh(fd, pw); } } } @@ -8672,28 +8668,32 @@ public class ConnectivityService extends IConnectivityManager.Stub public void factoryReset() { enforceSettingsPermission(); - if (mUserManager.hasUserRestriction(UserManager.DISALLOW_NETWORK_RESET)) { - return; - } - + final int uid = mDeps.getCallingUid(); final long token = Binder.clearCallingIdentity(); try { + if (mUserManager.hasUserRestrictionForUser(UserManager.DISALLOW_NETWORK_RESET, + UserHandle.getUserHandleForUid(uid))) { + return; + } + final IpMemoryStore ipMemoryStore = IpMemoryStore.getMemoryStore(mContext); ipMemoryStore.factoryReset(); - } finally { - Binder.restoreCallingIdentity(token); - } - // Turn airplane mode off - setAirplaneMode(false); + // Turn airplane mode off + setAirplaneMode(false); - // restore private DNS settings to default mode (opportunistic) - if (!mUserManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_PRIVATE_DNS)) { - ConnectivitySettingsManager.setPrivateDnsMode(mContext, PRIVATE_DNS_MODE_OPPORTUNISTIC); - } + // restore private DNS settings to default mode (opportunistic) + if (!mUserManager.hasUserRestrictionForUser(UserManager.DISALLOW_CONFIG_PRIVATE_DNS, + UserHandle.getUserHandleForUid(uid))) { + ConnectivitySettingsManager.setPrivateDnsMode(mContext, + PRIVATE_DNS_MODE_OPPORTUNISTIC); + } - Settings.Global.putString(mContext.getContentResolver(), - ConnectivitySettingsManager.NETWORK_AVOID_BAD_WIFI, null); + Settings.Global.putString(mContext.getContentResolver(), + ConnectivitySettingsManager.NETWORK_AVOID_BAD_WIFI, null); + } finally { + Binder.restoreCallingIdentity(token); + } } @Override @@ -9175,36 +9175,49 @@ public class ConnectivityService extends IConnectivityManager.Stub return results; } - @VisibleForTesting - boolean checkConnectivityDiagnosticsPermissions( - int callbackPid, int callbackUid, NetworkAgentInfo nai, String callbackPackageName) { - if (checkNetworkStackPermission(callbackPid, callbackUid)) { - return true; - } - + private boolean hasLocationPermission(String packageName, int uid) { // LocationPermissionChecker#checkLocationPermission can throw SecurityException if the uid // and package name don't match. Throwing on the CS thread is not acceptable, so wrap the // call in a try-catch. try { if (!mLocationPermissionChecker.checkLocationPermission( - callbackPackageName, null /* featureId */, callbackUid, null /* message */)) { + packageName, null /* featureId */, uid, null /* message */)) { return false; } } catch (SecurityException e) { return false; } + return true; + } + + private boolean ownsVpnRunningOverNetwork(int uid, Network network) { for (NetworkAgentInfo virtual : mNetworkAgentInfos) { if (virtual.supportsUnderlyingNetworks() - && virtual.networkCapabilities.getOwnerUid() == callbackUid - && CollectionUtils.contains(virtual.declaredUnderlyingNetworks, nai.network)) { + && virtual.networkCapabilities.getOwnerUid() == uid + && CollectionUtils.contains(virtual.declaredUnderlyingNetworks, network)) { return true; } } + return false; + } + + @VisibleForTesting + boolean checkConnectivityDiagnosticsPermissions( + int callbackPid, int callbackUid, NetworkAgentInfo nai, String callbackPackageName) { + if (checkNetworkStackPermission(callbackPid, callbackUid)) { + return true; + } + // Administrator UIDs also contains the Owner UID final int[] administratorUids = nai.networkCapabilities.getAdministratorUids(); - return CollectionUtils.contains(administratorUids, callbackUid); + if (!CollectionUtils.contains(administratorUids, callbackUid) + && !ownsVpnRunningOverNetwork(callbackUid, nai.network)) { + return false; + } + + return hasLocationPermission(callbackPackageName, callbackUid); } @Override diff --git a/packages/Connectivity/tests/common/java/android/net/NetworkCapabilitiesTest.java b/packages/Connectivity/tests/common/java/android/net/NetworkCapabilitiesTest.java index d74b802c8729..a30d4f11863e 100644 --- a/packages/Connectivity/tests/common/java/android/net/NetworkCapabilitiesTest.java +++ b/packages/Connectivity/tests/common/java/android/net/NetworkCapabilitiesTest.java @@ -33,6 +33,7 @@ import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VPN; import static android.net.NetworkCapabilities.NET_CAPABILITY_OEM_PAID; import static android.net.NetworkCapabilities.NET_CAPABILITY_OEM_PRIVATE; import static android.net.NetworkCapabilities.NET_CAPABILITY_PARTIAL_CONNECTIVITY; +import static android.net.NetworkCapabilities.NET_CAPABILITY_TRUSTED; import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED; import static android.net.NetworkCapabilities.NET_CAPABILITY_WIFI_P2P; import static android.net.NetworkCapabilities.REDACT_FOR_ACCESS_FINE_LOCATION; @@ -1149,4 +1150,15 @@ public class NetworkCapabilitiesTest { assertEquals(Set.of(TEST_SUBID1), nc2.getSubscriptionIds()); } } + + @Test @IgnoreUpTo(Build.VERSION_CODES.R) + public void testBuilderWithoutDefaultCap() { + final NetworkCapabilities nc = + NetworkCapabilities.Builder.withoutDefaultCapabilities().build(); + assertFalse(nc.hasCapability(NET_CAPABILITY_NOT_RESTRICTED)); + assertFalse(nc.hasCapability(NET_CAPABILITY_TRUSTED)); + assertFalse(nc.hasCapability(NET_CAPABILITY_NOT_VPN)); + // Ensure test case fails if new net cap is added into default cap but no update here. + assertEquals(0, nc.getCapabilities().length); + } } diff --git a/packages/Connectivity/tests/common/java/android/net/apf/ApfCapabilitiesTest.java b/packages/Connectivity/tests/common/java/android/net/apf/ApfCapabilitiesTest.java index d50406fd3a1c..88996d925262 100644 --- a/packages/Connectivity/tests/common/java/android/net/apf/ApfCapabilitiesTest.java +++ b/packages/Connectivity/tests/common/java/android/net/apf/ApfCapabilitiesTest.java @@ -18,6 +18,7 @@ package android.net.apf; import static com.android.testutils.ParcelUtils.assertParcelSane; +import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotEquals; @@ -25,12 +26,17 @@ import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import android.content.Context; +import android.os.Build; import androidx.test.InstrumentationRegistry; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; +import com.android.testutils.DevSdkIgnoreRule; +import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo; + import org.junit.Before; +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; @@ -39,6 +45,9 @@ import java.util.Arrays; @RunWith(AndroidJUnit4.class) @SmallTest public class ApfCapabilitiesTest { + @Rule + public final DevSdkIgnoreRule mIgnoreRule = new DevSdkIgnoreRule(); + private Context mContext; @Before @@ -85,6 +94,17 @@ public class ApfCapabilitiesTest { assertEquals(shouldDrop8023Frames, actual); } + @Test @IgnoreUpTo(Build.VERSION_CODES.R) + public void testGetApfDrop8023Frames_S() { + // IpClient does not call getApfDrop8023Frames() since S, so any customization of the return + // value on S+ is a configuration error as it will not be used by IpClient. + assertTrue("android.R.bool.config_apfDrop802_3Frames has been modified to false, but " + + "starting from S its value is not used by IpClient. If the modification is " + + "intentional, use a runtime resource overlay for the NetworkStack package to " + + "overlay com.android.networkstack.R.bool.config_apfDrop802_3Frames instead.", + ApfCapabilities.getApfDrop8023Frames()); + } + @Test public void testGetApfEtherTypeBlackList() { // Get com.android.internal.R.array.config_apfEthTypeBlackList. The test cannot directly @@ -96,4 +116,17 @@ public class ApfCapabilitiesTest { assertNotNull(actual); assertTrue(Arrays.equals(blacklistedEtherTypeArray, actual)); } + + @Test @IgnoreUpTo(Build.VERSION_CODES.R) + public void testGetApfEtherTypeBlackList_S() { + // IpClient does not call getApfEtherTypeBlackList() since S, so any customization of the + // return value on S+ is a configuration error as it will not be used by IpClient. + assertArrayEquals("android.R.array.config_apfEthTypeBlackList has been modified, but " + + "starting from S its value is not used by IpClient. If the modification " + + "is intentional, use a runtime resource overlay for the NetworkStack " + + "package to overlay " + + "com.android.networkstack.R.array.config_apfEthTypeDenyList instead.", + new int[] { 0x88a2, 0x88a4, 0x88b8, 0x88cd, 0x88e3 }, + ApfCapabilities.getApfEtherTypeBlackList()); + } } diff --git a/packages/Connectivity/tests/unit/java/android/net/NetworkTemplateTest.kt b/packages/Connectivity/tests/unit/java/android/net/NetworkTemplateTest.kt index ab6b2f409867..cb39a0c819f2 100644 --- a/packages/Connectivity/tests/unit/java/android/net/NetworkTemplateTest.kt +++ b/packages/Connectivity/tests/unit/java/android/net/NetworkTemplateTest.kt @@ -40,7 +40,7 @@ import android.net.NetworkTemplate.OEM_MANAGED_YES import android.net.NetworkTemplate.SUBSCRIBER_ID_MATCH_RULE_EXACT import android.net.NetworkTemplate.buildTemplateWifi import android.net.NetworkTemplate.buildTemplateWifiWildcard -import android.net.NetworkTemplate.buildTemplateCarrier +import android.net.NetworkTemplate.buildTemplateCarrierMetered import android.net.NetworkTemplate.buildTemplateMobileWithRatType import android.telephony.TelephonyManager import com.android.testutils.assertParcelSane @@ -73,11 +73,12 @@ class NetworkTemplateTest { type: Int, subscriberId: String? = null, ssid: String? = null, - oemManaged: Int = OEM_NONE + oemManaged: Int = OEM_NONE, + metered: Boolean = true ): NetworkStateSnapshot { val lp = LinkProperties() val caps = NetworkCapabilities().apply { - setCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED, false) + setCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED, !metered) setCapability(NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING, true) setSSID(ssid) setCapability(NetworkCapabilities.NET_CAPABILITY_OEM_PAID, @@ -167,25 +168,38 @@ class NetworkTemplateTest { } @Test - fun testCarrierMatches() { - val templateCarrierImsi1 = buildTemplateCarrier(TEST_IMSI1) - - val identMobile1 = buildNetworkIdentity(mockContext, buildMobileNetworkState(TEST_IMSI1), - false, TelephonyManager.NETWORK_TYPE_UMTS) - val identMobile2 = buildNetworkIdentity(mockContext, buildMobileNetworkState(TEST_IMSI2), - false, TelephonyManager.NETWORK_TYPE_UMTS) - val identWifiSsid1 = buildNetworkIdentity( - mockContext, buildWifiNetworkState(null, TEST_SSID1), true, 0) - val identCarrierWifiImsi1 = buildNetworkIdentity( - mockContext, buildWifiNetworkState(TEST_IMSI1, TEST_SSID1), true, 0) - val identCarrierWifiImsi2 = buildNetworkIdentity( - mockContext, buildWifiNetworkState(TEST_IMSI2, TEST_SSID1), true, 0) - - templateCarrierImsi1.assertMatches(identCarrierWifiImsi1) - templateCarrierImsi1.assertDoesNotMatch(identCarrierWifiImsi2) - templateCarrierImsi1.assertDoesNotMatch(identWifiSsid1) - templateCarrierImsi1.assertMatches(identMobile1) - templateCarrierImsi1.assertDoesNotMatch(identMobile2) + fun testCarrierMeteredMatches() { + val templateCarrierImsi1Metered = buildTemplateCarrierMetered(TEST_IMSI1) + + val mobileImsi1 = buildMobileNetworkState(TEST_IMSI1) + val mobileImsi1Unmetered = buildNetworkState(TYPE_MOBILE, TEST_IMSI1, null /* ssid */, + OEM_NONE, false /* metered */) + val mobileImsi2 = buildMobileNetworkState(TEST_IMSI2) + val wifiSsid1 = buildWifiNetworkState(null /* subscriberId */, TEST_SSID1) + val wifiImsi1Ssid1 = buildWifiNetworkState(TEST_IMSI1, TEST_SSID1) + val wifiImsi1Ssid1Unmetered = buildNetworkState(TYPE_WIFI, TEST_IMSI1, TEST_SSID1, + OEM_NONE, false /* metered */) + + val identMobileImsi1Metered = buildNetworkIdentity(mockContext, + mobileImsi1, false /* defaultNetwork */, TelephonyManager.NETWORK_TYPE_UMTS) + val identMobileImsi1Unmetered = buildNetworkIdentity(mockContext, + mobileImsi1Unmetered, false /* defaultNetwork */, + TelephonyManager.NETWORK_TYPE_UMTS) + val identMobileImsi2Metered = buildNetworkIdentity(mockContext, + mobileImsi2, false /* defaultNetwork */, TelephonyManager.NETWORK_TYPE_UMTS) + val identWifiSsid1Metered = buildNetworkIdentity( + mockContext, wifiSsid1, true /* defaultNetwork */, 0 /* subType */) + val identCarrierWifiImsi1Metered = buildNetworkIdentity( + mockContext, wifiImsi1Ssid1, true /* defaultNetwork */, 0 /* subType */) + val identCarrierWifiImsi1NonMetered = buildNetworkIdentity(mockContext, + wifiImsi1Ssid1Unmetered, true /* defaultNetwork */, 0 /* subType */) + + templateCarrierImsi1Metered.assertMatches(identMobileImsi1Metered) + templateCarrierImsi1Metered.assertDoesNotMatch(identMobileImsi1Unmetered) + templateCarrierImsi1Metered.assertDoesNotMatch(identMobileImsi2Metered) + templateCarrierImsi1Metered.assertDoesNotMatch(identWifiSsid1Metered) + templateCarrierImsi1Metered.assertMatches(identCarrierWifiImsi1Metered) + templateCarrierImsi1Metered.assertDoesNotMatch(identCarrierWifiImsi1NonMetered) } @Test diff --git a/packages/Connectivity/tests/unit/java/com/android/server/ConnectivityServiceTest.java b/packages/Connectivity/tests/unit/java/com/android/server/ConnectivityServiceTest.java index 41458f16baa4..1b4f8364b5be 100644 --- a/packages/Connectivity/tests/unit/java/com/android/server/ConnectivityServiceTest.java +++ b/packages/Connectivity/tests/unit/java/com/android/server/ConnectivityServiceTest.java @@ -9743,28 +9743,32 @@ public class ConnectivityServiceTest { @Test public void testCheckConnectivityDiagnosticsPermissionsWrongUidPackageName() throws Exception { - final NetworkAgentInfo naiWithoutUid = fakeMobileNai(new NetworkCapabilities()); + final int wrongUid = Process.myUid() + 1; + + final NetworkCapabilities nc = new NetworkCapabilities(); + nc.setAdministratorUids(new int[] {wrongUid}); + final NetworkAgentInfo naiWithUid = fakeMobileNai(nc); mServiceContext.setPermission(android.Manifest.permission.NETWORK_STACK, PERMISSION_DENIED); assertFalse( "Mismatched uid/package name should not pass the location permission check", mService.checkConnectivityDiagnosticsPermissions( - Process.myPid() + 1, Process.myUid() + 1, naiWithoutUid, - mContext.getOpPackageName())); + Process.myPid() + 1, wrongUid, naiWithUid, mContext.getOpPackageName())); } @Test public void testCheckConnectivityDiagnosticsPermissionsNoLocationPermission() throws Exception { - final NetworkAgentInfo naiWithoutUid = fakeMobileNai(new NetworkCapabilities()); + final NetworkCapabilities nc = new NetworkCapabilities(); + nc.setAdministratorUids(new int[] {Process.myUid()}); + final NetworkAgentInfo naiWithUid = fakeMobileNai(nc); mServiceContext.setPermission(android.Manifest.permission.NETWORK_STACK, PERMISSION_DENIED); assertFalse( "ACCESS_FINE_LOCATION permission necessary for Connectivity Diagnostics", mService.checkConnectivityDiagnosticsPermissions( - Process.myPid(), Process.myUid(), naiWithoutUid, - mContext.getOpPackageName())); + Process.myPid(), Process.myUid(), naiWithUid, mContext.getOpPackageName())); } @Test diff --git a/packages/Connectivity/tests/unit/java/com/android/server/net/NetworkStatsFactoryTest.java b/packages/Connectivity/tests/unit/java/com/android/server/net/NetworkStatsFactoryTest.java index f3ae9b051e7c..93599f3c376d 100644 --- a/packages/Connectivity/tests/unit/java/com/android/server/net/NetworkStatsFactoryTest.java +++ b/packages/Connectivity/tests/unit/java/com/android/server/net/NetworkStatsFactoryTest.java @@ -43,6 +43,7 @@ import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; import com.android.frameworks.tests.net.R; +import com.android.internal.util.test.FsUtil; import libcore.io.IoUtils; import libcore.io.Streams; @@ -71,7 +72,7 @@ public class NetworkStatsFactoryTest extends NetworkStatsBaseTest { public void setUp() throws Exception { mTestProc = new File(InstrumentationRegistry.getContext().getFilesDir(), "proc"); if (mTestProc.exists()) { - IoUtils.deleteContents(mTestProc); + FsUtil.deleteContents(mTestProc); } // The libandroid_servers which have the native method is not available to @@ -87,7 +88,7 @@ public class NetworkStatsFactoryTest extends NetworkStatsBaseTest { mFactory = null; if (mTestProc.exists()) { - IoUtils.deleteContents(mTestProc); + FsUtil.deleteContents(mTestProc); } } diff --git a/packages/Connectivity/tests/unit/java/com/android/server/net/NetworkStatsServiceTest.java b/packages/Connectivity/tests/unit/java/com/android/server/net/NetworkStatsServiceTest.java index fe2985c2ee54..0ba5f7d8241e 100644 --- a/packages/Connectivity/tests/unit/java/com/android/server/net/NetworkStatsServiceTest.java +++ b/packages/Connectivity/tests/unit/java/com/android/server/net/NetworkStatsServiceTest.java @@ -112,13 +112,12 @@ import androidx.test.runner.AndroidJUnit4; import com.android.internal.util.ArrayUtils; import com.android.internal.util.test.BroadcastInterceptingContext; +import com.android.internal.util.test.FsUtil; import com.android.server.net.NetworkStatsService.NetworkStatsSettings; import com.android.server.net.NetworkStatsService.NetworkStatsSettings.Config; import com.android.testutils.HandlerUtils; import com.android.testutils.TestableNetworkStatsProviderBinder; -import libcore.io.IoUtils; - import org.junit.After; import org.junit.Before; import org.junit.Ignore; @@ -152,6 +151,8 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest { private static final String TEST_SSID = "AndroidAP"; private static NetworkTemplate sTemplateWifi = buildTemplateWifi(TEST_SSID); + private static NetworkTemplate sTemplateCarrierWifi1 = + buildTemplateWifi(NetworkTemplate.WIFI_NETWORKID_ALL, IMSI_1); private static NetworkTemplate sTemplateImsi1 = buildTemplateMobileAll(IMSI_1); private static NetworkTemplate sTemplateImsi2 = buildTemplateMobileAll(IMSI_2); @@ -213,7 +214,7 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest { mServiceContext = new MockContext(context); mStatsDir = context.getFilesDir(); if (mStatsDir.exists()) { - IoUtils.deleteContents(mStatsDir); + FsUtil.deleteContents(mStatsDir); } PowerManager powerManager = (PowerManager) mServiceContext.getSystemService( @@ -283,7 +284,7 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest { @After public void tearDown() throws Exception { - IoUtils.deleteContents(mStatsDir); + FsUtil.deleteContents(mStatsDir); mServiceContext = null; mStatsDir = null; @@ -297,45 +298,82 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest { mHandlerThread.quitSafely(); } - @Test - public void testNetworkStatsWifi() throws Exception { + private void initWifiStats(NetworkStateSnapshot snapshot) throws Exception { // pretend that wifi network comes online; service should ask about full // network state, and poll any existing interfaces before updating. expectDefaultSettings(); - NetworkStateSnapshot[] states = new NetworkStateSnapshot[] {buildWifiState()}; + NetworkStateSnapshot[] states = new NetworkStateSnapshot[] {snapshot}; expectNetworkStatsSummary(buildEmptyStats()); expectNetworkStatsUidDetail(buildEmptyStats()); mService.notifyNetworkStatus(NETWORKS_WIFI, states, getActiveIface(states), new UnderlyingNetworkInfo[0]); + } - // verify service has empty history for wifi - assertNetworkTotal(sTemplateWifi, 0L, 0L, 0L, 0L, 0); - - // modify some number on wifi, and trigger poll event - incrementCurrentTime(HOUR_IN_MILLIS); + private void incrementWifiStats(long durationMillis, String iface, + long rxb, long rxp, long txb, long txp) throws Exception { + incrementCurrentTime(durationMillis); expectDefaultSettings(); expectNetworkStatsSummary(new NetworkStats(getElapsedRealtime(), 1) - .insertEntry(TEST_IFACE, 1024L, 1L, 2048L, 2L)); + .insertEntry(iface, rxb, rxp, txb, txp)); expectNetworkStatsUidDetail(buildEmptyStats()); forcePollAndWaitForIdle(); + } + + @Test + public void testNetworkStatsCarrierWifi() throws Exception { + initWifiStats(buildWifiState(true, TEST_IFACE, IMSI_1)); + // verify service has empty history for carrier merged wifi and non-carrier wifi + assertNetworkTotal(sTemplateCarrierWifi1, 0L, 0L, 0L, 0L, 0); + assertNetworkTotal(sTemplateWifi, 0L, 0L, 0L, 0L, 0); + + // modify some number on wifi, and trigger poll event + incrementWifiStats(HOUR_IN_MILLIS, TEST_IFACE, 1024L, 1L, 2048L, 2L); // verify service recorded history - assertNetworkTotal(sTemplateWifi, 1024L, 1L, 2048L, 2L, 0); + assertNetworkTotal(sTemplateCarrierWifi1, 1024L, 1L, 2048L, 2L, 0); + + // verify service recorded history for wifi with SSID filter + assertNetworkTotal(sTemplateWifi, 1024L, 1L, 2048L, 2L, 0); // and bump forward again, with counters going higher. this is // important, since polling should correctly subtract last snapshot. - incrementCurrentTime(DAY_IN_MILLIS); - expectDefaultSettings(); - expectNetworkStatsSummary(new NetworkStats(getElapsedRealtime(), 1) - .insertEntry(TEST_IFACE, 4096L, 4L, 8192L, 8L)); - expectNetworkStatsUidDetail(buildEmptyStats()); - forcePollAndWaitForIdle(); + incrementWifiStats(DAY_IN_MILLIS, TEST_IFACE, 4096L, 4L, 8192L, 8L); // verify service recorded history + assertNetworkTotal(sTemplateCarrierWifi1, 4096L, 4L, 8192L, 8L, 0); + // verify service recorded history for wifi with SSID filter assertNetworkTotal(sTemplateWifi, 4096L, 4L, 8192L, 8L, 0); + } + + @Test + public void testNetworkStatsNonCarrierWifi() throws Exception { + initWifiStats(buildWifiState()); + + // verify service has empty history for wifi + assertNetworkTotal(sTemplateWifi, 0L, 0L, 0L, 0L, 0); + // verify service has empty history for carrier merged wifi + assertNetworkTotal(sTemplateCarrierWifi1, 0L, 0L, 0L, 0L, 0); + + // modify some number on wifi, and trigger poll event + incrementWifiStats(HOUR_IN_MILLIS, TEST_IFACE, 1024L, 1L, 2048L, 2L); + // verify service recorded history + assertNetworkTotal(sTemplateWifi, 1024L, 1L, 2048L, 2L, 0); + // verify service has empty history for carrier wifi since current network is non carrier + // wifi + assertNetworkTotal(sTemplateCarrierWifi1, 0L, 0L, 0L, 0L, 0); + + // and bump forward again, with counters going higher. this is + // important, since polling should correctly subtract last snapshot. + incrementWifiStats(DAY_IN_MILLIS, TEST_IFACE, 4096L, 4L, 8192L, 8L); + + // verify service recorded history + assertNetworkTotal(sTemplateWifi, 4096L, 4L, 8192L, 8L, 0); + // verify service has empty history for carrier wifi since current network is non carrier + // wifi + assertNetworkTotal(sTemplateCarrierWifi1, 0L, 0L, 0L, 0L, 0); } @Test @@ -1662,10 +1700,15 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest { } private static NetworkStateSnapshot buildWifiState() { - return buildWifiState(false, TEST_IFACE); + return buildWifiState(false, TEST_IFACE, null); } private static NetworkStateSnapshot buildWifiState(boolean isMetered, @NonNull String iface) { + return buildWifiState(isMetered, iface, null); + } + + private static NetworkStateSnapshot buildWifiState(boolean isMetered, @NonNull String iface, + String subscriberId) { final LinkProperties prop = new LinkProperties(); prop.setInterfaceName(iface); final NetworkCapabilities capabilities = new NetworkCapabilities(); @@ -1673,7 +1716,7 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest { capabilities.setCapability(NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING, true); capabilities.addTransportType(NetworkCapabilities.TRANSPORT_WIFI); capabilities.setSSID(TEST_SSID); - return new NetworkStateSnapshot(WIFI_NETWORK, capabilities, prop, null, TYPE_WIFI); + return new NetworkStateSnapshot(WIFI_NETWORK, capabilities, prop, subscriberId, TYPE_WIFI); } private static NetworkStateSnapshot buildMobile3gState(String subscriberId) { diff --git a/packages/CtsShim/OWNERS b/packages/CtsShim/OWNERS index ba9f2b97678a..94197715150d 100644 --- a/packages/CtsShim/OWNERS +++ b/packages/CtsShim/OWNERS @@ -1,2 +1,3 @@ ioffe@google.com -toddke@google.com
\ No newline at end of file +toddke@google.com +patb@google.com
\ No newline at end of file diff --git a/packages/PackageInstaller/OWNERS b/packages/PackageInstaller/OWNERS index 8e1774b0baa2..c6331133367a 100644 --- a/packages/PackageInstaller/OWNERS +++ b/packages/PackageInstaller/OWNERS @@ -1,5 +1,6 @@ svetoslavganov@google.com toddke@google.com +patb@google.com suprabh@google.com # For automotive related changes diff --git a/packages/SettingsProvider/OWNERS b/packages/SettingsProvider/OWNERS index cf9799c6a026..6c61d4b91d36 100644 --- a/packages/SettingsProvider/OWNERS +++ b/packages/SettingsProvider/OWNERS @@ -4,3 +4,4 @@ narayan@google.com svetoslavganov@google.com schfan@google.com toddke@google.com +patb@google.com diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml index 2a12ce2854b6..133a839bdc94 100644 --- a/packages/Shell/AndroidManifest.xml +++ b/packages/Shell/AndroidManifest.xml @@ -427,8 +427,9 @@ <!-- Permission needed for CTS test - DisplayTest --> <uses-permission android:name="android.permission.OVERRIDE_DISPLAY_MODE_REQUESTS" /> - <!-- Permission needed for CTS test - TimeManagerTest --> + <!-- Permissions needed for CTS test - TimeManagerTest --> <uses-permission android:name="android.permission.MANAGE_TIME_AND_ZONE_DETECTION" /> + <uses-permission android:name="android.permission.SUGGEST_EXTERNAL_TIME" /> <!-- Permission needed for CTS test - CtsHdmiCecHostTestCases --> <uses-permission android:name="android.permission.HDMI_CEC" /> diff --git a/packages/Shell/OWNERS b/packages/Shell/OWNERS index 6d738f8d4d43..177f86b08864 100644 --- a/packages/Shell/OWNERS +++ b/packages/Shell/OWNERS @@ -7,6 +7,7 @@ svetoslavganov@google.com hackbod@google.com yamasani@google.com toddke@google.com +patb@google.com cbrubaker@google.com omakoto@google.com michaelwr@google.com diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt index 5a525974f3cb..9e603561acf1 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt @@ -114,7 +114,7 @@ class ControlsUiControllerImpl @Inject constructor ( private val onSeedingComplete = Consumer<Boolean> { accepted -> if (accepted) { - selectedStructure = controlsController.get().getFavorites().maxBy { + selectedStructure = controlsController.get().getFavorites().maxByOrNull { it.controls.size } ?: EMPTY_STRUCTURE updatePreferences(selectedStructure) diff --git a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyChipBuilder.kt b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyChipBuilder.kt index 1d2e74703b42..eec69f98b9be 100644 --- a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyChipBuilder.kt +++ b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyChipBuilder.kt @@ -28,7 +28,7 @@ class PrivacyChipBuilder(private val context: Context, itemsList: List<PrivacyIt appsAndTypes = itemsList.groupBy({ it.application }, { it.privacyType }) .toList() .sortedWith(compareBy({ -it.second.size }, // Sort by number of AppOps - { it.second.min() })) // Sort by "smallest" AppOpp (Location is largest) + { it.second.minOrNull() })) // Sort by "smallest" AppOpp (Location is largest) types = itemsList.map { it.privacyType }.distinct().sorted() } diff --git a/services/OWNERS b/services/OWNERS index 03e0807eea62..3b972e922e95 100644 --- a/services/OWNERS +++ b/services/OWNERS @@ -3,4 +3,4 @@ per-file Android.bp = file:platform/build/soong:/OWNERS # art-team@ manages the system server profile per-file art-profile* = calin@google.com, mathieuc@google.com, ngeoffray@google.com -per-file java/com/android/server/* = toddke@google.com +per-file java/com/android/server/* = toddke@google.com,patb@google.com diff --git a/services/core/java/android/os/BatteryStatsInternal.java b/services/core/java/android/os/BatteryStatsInternal.java index 679f18e1e860..7cf5fd621f18 100644 --- a/services/core/java/android/os/BatteryStatsInternal.java +++ b/services/core/java/android/os/BatteryStatsInternal.java @@ -16,6 +16,10 @@ package android.os; +import com.android.internal.os.BinderCallsStats; + +import java.util.Collection; + /** * Battery stats local system service interface. This is used to pass internal data out of * BatteryStatsImpl, as well as make unchecked calls into BatteryStatsImpl. @@ -41,4 +45,10 @@ public abstract class BatteryStatsInternal { * @param sinceLast how long in millis has it been since a job was run */ public abstract void noteJobsDeferred(int uid, int numDeferred, long sinceLast); + + /** + * Informs battery stats of binder stats for the given work source UID. + */ + public abstract void noteBinderCallStats(int workSourceUid, long incrementalBinderCallCount, + Collection<BinderCallsStats.CallStat> callStats); } diff --git a/services/core/java/com/android/server/BinderCallsStatsService.java b/services/core/java/com/android/server/BinderCallsStatsService.java index f4a8f372bc36..65bb21c32117 100644 --- a/services/core/java/com/android/server/BinderCallsStatsService.java +++ b/services/core/java/com/android/server/BinderCallsStatsService.java @@ -26,6 +26,7 @@ import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; import android.database.ContentObserver; import android.net.Uri; +import android.os.BatteryStatsInternal; import android.os.Binder; import android.os.Process; import android.os.SystemProperties; @@ -133,6 +134,8 @@ public class BinderCallsStatsService extends Binder { private static final String SETTINGS_COLLECT_LATENCY_DATA_KEY = "collect_Latency_data"; private static final String SETTINGS_LATENCY_OBSERVER_SAMPLING_INTERVAL_KEY = "latency_observer_sampling_interval"; + private static final String SETTINGS_LATENCY_OBSERVER_PUSH_INTERVAL_MINUTES_KEY = + "latency_observer_push_interval_minutes"; private static final String SETTINGS_LATENCY_HISTOGRAM_BUCKET_COUNT_KEY = "latency_histogram_bucket_count"; private static final String SETTINGS_LATENCY_HISTOGRAM_FIRST_BUCKET_SIZE_KEY = @@ -173,10 +176,10 @@ public class BinderCallsStatsService extends Binder { } try { - mParser.setString(Settings.Global.getString(mContext.getContentResolver(), - Settings.Global.BINDER_CALLS_STATS)); + mParser.setString(Settings.Global.getString(mContext.getContentResolver(), + Settings.Global.BINDER_CALLS_STATS)); } catch (IllegalArgumentException e) { - Slog.e(TAG, "Bad binder call stats settings", e); + Slog.e(TAG, "Bad binder call stats settings", e); } mBinderCallsStats.setDetailedTracking(mParser.getBoolean( SETTINGS_DETAILED_TRACKING_KEY, BinderCallsStats.DETAILED_TRACKING_DEFAULT)); @@ -210,7 +213,9 @@ public class BinderCallsStatsService extends Binder { mParser.getFloat( SETTINGS_LATENCY_HISTOGRAM_BUCKET_SCALE_FACTOR_KEY, BinderLatencyObserver.BUCKET_SCALE_FACTOR_DEFAULT)); - + binderLatencyObserver.setPushInterval(mParser.getInt( + SETTINGS_LATENCY_OBSERVER_PUSH_INTERVAL_MINUTES_KEY, + BinderLatencyObserver.STATSD_PUSH_INTERVAL_MINUTES_DEFAULT)); final boolean enabled = mParser.getBoolean(SETTINGS_ENABLED_KEY, BinderCallsStats.ENABLED_DEFAULT); @@ -298,6 +303,11 @@ public class BinderCallsStatsService extends Binder { CachedDeviceState.Readonly deviceState = getLocalService( CachedDeviceState.Readonly.class); mBinderCallsStats.setDeviceState(deviceState); + + BatteryStatsInternal batteryStatsInternal = getLocalService( + BatteryStatsInternal.class); + mBinderCallsStats.setCallStatsObserver(batteryStatsInternal::noteBinderCallStats); + // It needs to be called before mService.systemReady to make sure the observer is // initialized before installing it. mWorkSourceProvider.systemReady(getContext()); diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 2744f11f1c4e..011a3568aff9 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -4060,13 +4060,19 @@ public class ActivityManagerService extends IActivityManager.Stub final int max = SystemProperties.getInt("tombstoned.max_anr_count", 64); final long now = System.currentTimeMillis(); - Arrays.sort(files, Comparator.comparingLong(File::lastModified).reversed()); - for (int i = 0; i < files.length; ++i) { - if (i > max || (now - files[i].lastModified()) > DAY_IN_MILLIS) { - if (!files[i].delete()) { - Slog.w(TAG, "Unable to prune stale trace file: " + files[i]); + try { + Arrays.sort(files, Comparator.comparingLong(File::lastModified).reversed()); + for (int i = 0; i < files.length; ++i) { + if (i > max || (now - files[i].lastModified()) > DAY_IN_MILLIS) { + if (!files[i].delete()) { + Slog.w(TAG, "Unable to prune stale trace file: " + files[i]); + } } } + } catch (IllegalArgumentException e) { + // The modification times changed while we were sorting. Bail... + // https://issuetracker.google.com/169836837 + Slog.w(TAG, "tombstone modification times changed while sorting; not pruning", e); } } @@ -10272,11 +10278,13 @@ public class ActivityManagerService extends IActivityManager.Stub if (lines > 0) { sb.append("\n"); - // Merge several logcat streams, and take the last N lines InputStreamReader input = null; try { java.lang.Process logcat = new ProcessBuilder( - "/system/bin/timeout", "-k", "15s", "10s", + // Time out after 10s, but kill logcat with SEGV + // so we can investigate why it didn't finish. + "/system/bin/timeout", "-s", "SEGV", "10s", + // Merge several logcat streams, and take the last N lines. "/system/bin/logcat", "-v", "threadtime", "-b", "events", "-b", "system", "-b", "main", "-b", "crash", "-t", String.valueOf(lines)) .redirectErrorStream(true).start(); diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java index 226802c74f25..b45237caec26 100644 --- a/services/core/java/com/android/server/am/BatteryStatsService.java +++ b/services/core/java/com/android/server/am/BatteryStatsService.java @@ -66,6 +66,7 @@ import com.android.internal.annotations.GuardedBy; import com.android.internal.app.IBatteryStats; import com.android.internal.os.BatteryStatsHelper; import com.android.internal.os.BatteryStatsImpl; +import com.android.internal.os.BinderCallsStats; import com.android.internal.os.PowerProfile; import com.android.internal.os.RailStats; import com.android.internal.os.RpmStats; @@ -87,6 +88,7 @@ import java.nio.charset.CharsetDecoder; import java.nio.charset.CodingErrorAction; import java.nio.charset.StandardCharsets; import java.util.Arrays; +import java.util.Collection; import java.util.List; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; @@ -304,6 +306,12 @@ public final class BatteryStatsService extends IBatteryStats.Stub if (DBG) Slog.d(TAG, "Jobs deferred " + uid + ": " + numDeferred + " " + sinceLast); BatteryStatsService.this.noteJobsDeferred(uid, numDeferred, sinceLast); } + + @Override + public void noteBinderCallStats(int workSourceUid, long incrementatCallCount, + Collection<BinderCallsStats.CallStat> callStats) { + mStats.noteBinderCallStats(workSourceUid, incrementatCallCount, callStats); + } } private static void awaitUninterruptibly(Future<?> future) { @@ -1771,5 +1779,4 @@ public final class BatteryStatsService extends IBatteryStats.Stub Binder.restoreCallingIdentity(ident); } } - } diff --git a/services/core/java/com/android/server/am/OWNERS b/services/core/java/com/android/server/am/OWNERS index 90d940939be8..0c8114cdf2e2 100644 --- a/services/core/java/com/android/server/am/OWNERS +++ b/services/core/java/com/android/server/am/OWNERS @@ -20,6 +20,7 @@ ogunwale@google.com # Permissions & Packages svetoslavganov@google.com toddke@google.com +patb@google.com # Battery Stats joeo@google.com diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java index 115899a2a518..668d290cbad3 100644 --- a/services/core/java/com/android/server/input/InputManagerService.java +++ b/services/core/java/com/android/server/input/InputManagerService.java @@ -2026,8 +2026,7 @@ public class InputManagerService extends IInputManager.Stub }; for (File baseDir: baseDirs) { File confFile = new File(baseDir, EXCLUDED_DEVICES_PATH); - try { - InputStream stream = new FileInputStream(confFile); + try (InputStream stream = new FileInputStream(confFile)) { names.addAll(ConfigurationProcessor.processExcludedDeviceNames(stream)); } catch (FileNotFoundException e) { // It's ok if the file does not exist. @@ -2060,8 +2059,7 @@ public class InputManagerService extends IInputManager.Stub final File baseDir = Environment.getVendorDirectory(); final File confFile = new File(baseDir, PORT_ASSOCIATIONS_PATH); - try { - final InputStream stream = new FileInputStream(confFile); + try (final InputStream stream = new FileInputStream(confFile)) { return ConfigurationProcessor.processInputPortAssociations(stream); } catch (FileNotFoundException e) { // Most of the time, file will not exist, which is expected. @@ -2173,10 +2171,10 @@ public class InputManagerService extends IInputManager.Stub @Override public void visitKeyboardLayout(Resources resources, int keyboardLayoutResId, KeyboardLayout layout) { - try { + try (final InputStreamReader stream = new InputStreamReader( + resources.openRawResource(keyboardLayoutResId))) { result[0] = layout.getDescriptor(); - result[1] = Streams.readFully(new InputStreamReader( - resources.openRawResource(keyboardLayoutResId))); + result[1] = Streams.readFully(stream); } catch (IOException ex) { } catch (NotFoundException ex) { } diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 0d06426a7ab5..a1560b5f0298 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -9903,10 +9903,10 @@ public class PackageManagerService extends IPackageManager.Stub @Override public void notifyDexLoad(String loadingPackageName, Map<String, String> classLoaderContextMap, String loaderIsa) { - if (PLATFORM_PACKAGE_NAME.equals(loadingPackageName) - && Binder.getCallingUid() != Process.SYSTEM_UID) { + int callingUid = Binder.getCallingUid(); + if (PLATFORM_PACKAGE_NAME.equals(loadingPackageName) && callingUid != Process.SYSTEM_UID) { Slog.w(TAG, "Non System Server process reporting dex loads as system server. uid=" - + Binder.getCallingUid()); + + callingUid); // Do not record dex loads from processes pretending to be system server. // Only the system server should be assigned the package "android", so reject calls // that don't satisfy the constraint. @@ -9917,6 +9917,7 @@ public class PackageManagerService extends IPackageManager.Stub // in order to verify the expectations. return; } + int userId = UserHandle.getCallingUserId(); ApplicationInfo ai = getApplicationInfo(loadingPackageName, /*flags*/ 0, userId); if (ai == null) { @@ -9924,7 +9925,8 @@ public class PackageManagerService extends IPackageManager.Stub + loadingPackageName + ", user=" + userId); return; } - mDexManager.notifyDexLoad(ai, classLoaderContextMap, loaderIsa, userId); + mDexManager.notifyDexLoad(ai, classLoaderContextMap, loaderIsa, userId, + Process.isIsolated(callingUid)); } @Override diff --git a/services/core/java/com/android/server/pm/dex/DexManager.java b/services/core/java/com/android/server/pm/dex/DexManager.java index 37f317557aeb..32ba26c2d5ed 100644 --- a/services/core/java/com/android/server/pm/dex/DexManager.java +++ b/services/core/java/com/android/server/pm/dex/DexManager.java @@ -86,6 +86,11 @@ public class DexManager { // However it can load verification data - thus we pick the "verify" compiler filter. private static final String SYSTEM_SERVER_COMPILER_FILTER = "verify"; + // The suffix we add to the package name when the loading happens in an isolated process. + // Note that the double dot creates and "invalid" package name which makes it clear that this + // is an artificially constructed name. + private static final String ISOLATED_PROCESS_PACKAGE_SUFFIX = "..isolated"; + private final Context mContext; // Maps package name to code locations. @@ -166,12 +171,14 @@ public class DexManager { * the class loader context that was used to load them. * @param loaderIsa the ISA of the app loading the dex files * @param loaderUserId the user id which runs the code loading the dex files + * @param loaderIsIsolatedProcess whether or not the loading process is isolated. */ public void notifyDexLoad(ApplicationInfo loadingAppInfo, - Map<String, String> classLoaderContextMap, String loaderIsa, int loaderUserId) { + Map<String, String> classLoaderContextMap, String loaderIsa, int loaderUserId, + boolean loaderIsIsolatedProcess) { try { notifyDexLoadInternal(loadingAppInfo, classLoaderContextMap, loaderIsa, - loaderUserId); + loaderUserId, loaderIsIsolatedProcess); } catch (Exception e) { Slog.w(TAG, "Exception while notifying dex load for package " + loadingAppInfo.packageName, e); @@ -181,7 +188,7 @@ public class DexManager { @VisibleForTesting /*package*/ void notifyDexLoadInternal(ApplicationInfo loadingAppInfo, Map<String, String> classLoaderContextMap, String loaderIsa, - int loaderUserId) { + int loaderUserId, boolean loaderIsIsolatedProcess) { if (classLoaderContextMap == null) { return; } @@ -195,22 +202,36 @@ public class DexManager { return; } + // If this load is coming from an isolated process we need to be able to prevent profile + // based optimizations. This is because isolated processes are sandboxed and can only read + // world readable files, so they need world readable optimization files. An + // example of such a package is webview. + // + // In order to prevent profile optimization we pretend that the load is coming from a + // different package, and so we assign a artificial name to the loading package making it + // clear that it comes from an isolated process. This blends well with the entire + // usedByOthers logic without needing to special handle isolated process in all dexopt + // layers. + String loadingPackageAmendedName = loadingAppInfo.packageName; + if (loaderIsIsolatedProcess) { + loadingPackageAmendedName += ISOLATED_PROCESS_PACKAGE_SUFFIX; + } for (Map.Entry<String, String> mapping : classLoaderContextMap.entrySet()) { String dexPath = mapping.getKey(); // Find the owning package name. DexSearchResult searchResult = getDexPackage(loadingAppInfo, dexPath, loaderUserId); if (DEBUG) { - Slog.i(TAG, loadingAppInfo.packageName - + " loads from " + searchResult + " : " + loaderUserId + " : " + dexPath); + Slog.i(TAG, loadingPackageAmendedName + + " loads from " + searchResult + " : " + loaderUserId + " : " + dexPath); } if (searchResult.mOutcome != DEX_SEARCH_NOT_FOUND) { // TODO(calin): extend isUsedByOtherApps check to detect the cases where // different apps share the same runtime. In that case we should not mark the dex // file as isUsedByOtherApps. Currently this is a safe approximation. - boolean isUsedByOtherApps = !loadingAppInfo.packageName.equals( - searchResult.mOwningPackageName); + boolean isUsedByOtherApps = + !loadingPackageAmendedName.equals(searchResult.mOwningPackageName); boolean primaryOrSplit = searchResult.mOutcome == DEX_SEARCH_FOUND_PRIMARY || searchResult.mOutcome == DEX_SEARCH_FOUND_SPLIT; @@ -249,7 +270,7 @@ public class DexManager { // async write to disk to make sure we don't loose the data in case of a reboot. if (mPackageDexUsage.record(searchResult.mOwningPackageName, dexPath, loaderUserId, loaderIsa, primaryOrSplit, - loadingAppInfo.packageName, classLoaderContext, overwriteCLC)) { + loadingPackageAmendedName, classLoaderContext, overwriteCLC)) { mPackageDexUsage.maybeWriteAsync(); } } @@ -749,7 +770,7 @@ public class DexManager { dexPath, userId, isa, /*primaryOrSplit*/ false, loadingPackage, PackageDexUsage.VARIABLE_CLASS_LOADER_CONTEXT, - /*overwriteCLC*/ false); + /*overwriteCLC=*/ false); update |= newUpdate; } if (update) { diff --git a/services/incremental/OWNERS b/services/incremental/OWNERS index ad5eca7f6daf..7ebb962c8feb 100644 --- a/services/incremental/OWNERS +++ b/services/incremental/OWNERS @@ -5,3 +5,4 @@ alexbuy@google.com schfan@google.com toddke@google.com zyy@google.com +patb@google.com diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/dex/DexManagerTests.java b/services/tests/mockingservicestests/src/com/android/server/pm/dex/DexManagerTests.java index 2e0cadf264cf..8abe46fab1d6 100644 --- a/services/tests/mockingservicestests/src/com/android/server/pm/dex/DexManagerTests.java +++ b/services/tests/mockingservicestests/src/com/android/server/pm/dex/DexManagerTests.java @@ -410,6 +410,17 @@ public class DexManagerTests { } @Test + public void testNotifyUsedByIsolatedProcess() { + // Bar loads its own apk but as isolatedProcess. + notifyDexLoad(mBarUser0, mBarUser0.getBaseAndSplitDexPaths(), mUser0, + /*isolatedProcess=*/ true); + + // Bar is used by an isolated process and should be marked as usedByOtherApps + PackageUseInfo pui = getPackageUseInfo(mBarUser0); + assertIsUsedByOtherApps(mBarUser0, pui, true); + } + + @Test public void testNotifyPackageUpdatedCodeLocations() { // Simulate a split update. String newSplit = mBarUser0.replaceLastSplit(); @@ -545,7 +556,7 @@ public class DexManagerTests { List<String> classLoaders = Arrays.asList(PATH_CLASS_LOADER_NAME, UNSUPPORTED_CLASS_LOADER_NAME); List<String> classPaths = Arrays.asList(classPath, classPath); - notifyDexLoad(mBarUser0, classLoaders, classPaths, mUser0); + notifyDexLoad(mBarUser0, classLoaders, classPaths, mUser0, /*isolatedProcess=*/ false); assertNoUseInfo(mBarUser0); @@ -664,7 +675,8 @@ public class DexManagerTests { expectedContexts[i] += contextSuffix; } - notifyDexLoad(mFooUser0, fooSecondaries, expectedContexts, mUser0); + notifyDexLoad(mFooUser0, fooSecondaries, expectedContexts, mUser0, + /*isolatedProcess=*/ false); PackageUseInfo pui = getPackageUseInfo(mFooUser0); assertIsUsedByOtherApps(mFooUser0, pui, false); @@ -838,26 +850,32 @@ public class DexManagerTests { assertEquals(codePath, isUsedByOtherApps, pui.isUsedByOtherApps(codePath)); } } + private void notifyDexLoad(TestData testData, List<String> dexPaths, int loaderUserId) { + notifyDexLoad(testData, dexPaths, loaderUserId, /*isolatedProcess=*/ false); + } + + private void notifyDexLoad(TestData testData, List<String> dexPaths, int loaderUserId, + boolean isolatedProcess) { // By default, assume a single class loader in the chain. // This makes writing tests much easier. List<String> classLoaders = Arrays.asList(testData.mClassLoader); List<String> classPaths = dexPaths != null ? Arrays.<String>asList(String.join(File.pathSeparator, dexPaths)) : null; - notifyDexLoad(testData, classLoaders, classPaths, loaderUserId); + notifyDexLoad(testData, classLoaders, classPaths, loaderUserId, isolatedProcess); } private void notifyDexLoad(TestData testData, List<String> classLoaders, - List<String> classPaths, int loaderUserId) { + List<String> classPaths, int loaderUserId, boolean isolatedProcess) { String[] classLoaderContexts = computeClassLoaderContexts(classLoaders, classPaths); // We call the internal function so any exceptions thrown cause test failures. List<String> dexPaths = classPaths != null ? Arrays.asList(classPaths.get(0).split(File.pathSeparator)) : Arrays.asList(); - notifyDexLoad(testData, dexPaths, classLoaderContexts, loaderUserId); + notifyDexLoad(testData, dexPaths, classLoaderContexts, loaderUserId, isolatedProcess); } private void notifyDexLoad(TestData testData, List<String> dexPaths, - String[] classLoaderContexts, int loaderUserId) { + String[] classLoaderContexts, int loaderUserId, boolean isolatedProcess) { assertTrue(dexPaths.size() == classLoaderContexts.length); HashMap<String, String> dexPathMapping = new HashMap<>(dexPaths.size()); for (int i = 0; i < dexPaths.size(); i++) { @@ -865,7 +883,7 @@ public class DexManagerTests { ? classLoaderContexts[i] : PackageDexUsage.UNSUPPORTED_CLASS_LOADER_CONTEXT); } mDexManager.notifyDexLoadInternal(testData.mPackageInfo.applicationInfo, dexPathMapping, - testData.mLoaderIsa, loaderUserId); + testData.mLoaderIsa, loaderUserId, isolatedProcess); } private String[] computeClassLoaderContexts(List<String> classLoaders, diff --git a/services/tests/servicestests/src/com/android/server/job/JobStoreTest.java b/services/tests/servicestests/src/com/android/server/job/JobStoreTest.java index a1bce52fe63a..9a5237ca96d5 100644 --- a/services/tests/servicestests/src/com/android/server/job/JobStoreTest.java +++ b/services/tests/servicestests/src/com/android/server/job/JobStoreTest.java @@ -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; @@ -102,6 +104,20 @@ public class JobStoreTest { } @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; long runByMillis = 20000L; // 20s diff --git a/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java index a02a039c3beb..d041eecab17b 100644 --- a/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java @@ -159,13 +159,13 @@ import androidx.test.runner.AndroidJUnit4; import com.android.internal.util.test.BroadcastInterceptingContext; import com.android.internal.util.test.BroadcastInterceptingContext.FutureIntent; +import com.android.internal.util.test.FsUtil; import com.android.server.DeviceIdleInternal; import com.android.server.LocalServices; import com.android.server.usage.AppStandbyInternal; import com.google.common.util.concurrent.AbstractFuture; -import libcore.io.IoUtils; import libcore.io.Streams; import org.junit.After; @@ -2347,7 +2347,7 @@ public class NetworkPolicyManagerServiceTest { private void setNetpolicyXml(Context context) throws Exception { mPolicyDir = context.getFilesDir(); if (mPolicyDir.exists()) { - IoUtils.deleteContents(mPolicyDir); + FsUtil.deleteContents(mPolicyDir); } if (!TextUtils.isEmpty(mNetpolicyXml)) { final String assetPath = NETPOLICY_DIR + "/" + mNetpolicyXml; diff --git a/telecomm/java/android/telecom/PhoneAccount.java b/telecomm/java/android/telecom/PhoneAccount.java index 579b33e9c283..e332d3ff2b4d 100644 --- a/telecomm/java/android/telecom/PhoneAccount.java +++ b/telecomm/java/android/telecom/PhoneAccount.java @@ -27,6 +27,7 @@ import android.net.Uri; import android.os.Bundle; import android.os.Parcel; import android.os.Parcelable; +import android.telephony.CarrierConfigManager; import android.telephony.TelephonyManager; import android.text.TextUtils; @@ -283,10 +284,13 @@ public final class PhoneAccount implements Parcelable { * number relies on presence. Should only be set if the {@code PhoneAccount} also has * {@link #CAPABILITY_VIDEO_CALLING}. * <p> - * When set, the {@link ConnectionService} is responsible for toggling the + * Note: As of Android 12, using the * {@link android.provider.ContactsContract.Data#CARRIER_PRESENCE_VT_CAPABLE} bit on the * {@link android.provider.ContactsContract.Data#CARRIER_PRESENCE} column to indicate whether - * a contact's phone number supports video calling. + * a contact's phone number supports video calling has been deprecated and should only be used + * on devices where {@link CarrierConfigManager#KEY_USE_RCS_PRESENCE_BOOL} is set. On newer + * devices, applications must use {@link android.telephony.ims.RcsUceAdapter} instead to + * determine whether or not a contact's phone number supports carrier video calling. * <p> * See {@link #getCapabilities} */ diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java index f6ba7becc922..eb423b4a9b53 100644 --- a/telephony/java/android/telephony/CarrierConfigManager.java +++ b/telephony/java/android/telephony/CarrierConfigManager.java @@ -36,6 +36,7 @@ import android.telephony.gba.UaSecurityProtocolIdentifier; import android.telephony.ims.ImsReasonInfo; import android.telephony.ims.ImsRegistrationAttributes; import android.telephony.ims.ImsSsData; +import android.telephony.ims.RcsUceAdapter; import android.telephony.ims.feature.MmTelFeature; import android.telephony.ims.feature.RcsFeature; @@ -4071,6 +4072,9 @@ public class CarrierConfigManager { * If this flag is disabled, the capabilities cache will not be refreshed internally at all * and will only be updated if the cached capabilities are stale when an application * requests them. + * + * @see RcsUceAdapter#isUceSettingEnabled() more information about this feature and how + * it is enabled by the user. */ public static final String KEY_RCS_BULK_CAPABILITY_EXCHANGE_BOOL = KEY_PREFIX + "rcs_bulk_capability_exchange_bool"; diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java index ae6a3e846950..1860ecb7c274 100644 --- a/telephony/java/android/telephony/TelephonyManager.java +++ b/telephony/java/android/telephony/TelephonyManager.java @@ -14506,6 +14506,15 @@ public class TelephonyManager { public static final String CAPABILITY_THERMAL_MITIGATION_DATA_THROTTLING = "CAPABILITY_THERMAL_MITIGATION_DATA_THROTTLING"; + /** + * Indicates whether modem supports handling parsed SIM phonebook records through the RIL, + * both batched reads and individual writes. + * + * @hide + */ + public static final String CAPABILITY_SIM_PHONEBOOK_IN_MODEM = + "CAPABILITY_SIM_PHONEBOOK_IN_MODEM"; + /** @hide */ @Retention(RetentionPolicy.SOURCE) @StringDef(prefix = "CAPABILITY_", value = { @@ -14513,6 +14522,7 @@ public class TelephonyManager { CAPABILITY_ALLOWED_NETWORK_TYPES_USED, CAPABILITY_NR_DUAL_CONNECTIVITY_CONFIGURATION_AVAILABLE, CAPABILITY_THERMAL_MITIGATION_DATA_THROTTLING, + CAPABILITY_SIM_PHONEBOOK_IN_MODEM }) public @interface RadioInterfaceCapability {} diff --git a/telephony/java/android/telephony/ims/ImsRcsManager.java b/telephony/java/android/telephony/ims/ImsRcsManager.java index 5f4e1e6f3148..81d820731d05 100644 --- a/telephony/java/android/telephony/ims/ImsRcsManager.java +++ b/telephony/java/android/telephony/ims/ImsRcsManager.java @@ -56,7 +56,9 @@ public class ImsRcsManager { /** * Activity Action: Show the opt-in dialog for enabling or disabling RCS contact discovery - * using User Capability Exchange (UCE). + * using User Capability Exchange (UCE), which enables a service that periodically shares the + * phone numbers of all of the contacts in the user's address book with the carrier to refresh + * the RCS capabilities associated with those contacts as the local cache becomes stale. * <p> * An application that depends on RCS contact discovery being enabled must send this intent * using {@link Context#startActivity(Intent)} to ask the user to opt-in for contacts upload for diff --git a/telephony/java/android/telephony/ims/RcsUceAdapter.java b/telephony/java/android/telephony/ims/RcsUceAdapter.java index dd9102699529..7a1c09275a6d 100644 --- a/telephony/java/android/telephony/ims/RcsUceAdapter.java +++ b/telephony/java/android/telephony/ims/RcsUceAdapter.java @@ -28,10 +28,13 @@ import android.os.Binder; import android.os.IBinder; import android.os.RemoteException; import android.os.ServiceSpecificException; +import android.telephony.CarrierConfigManager; +import android.telephony.SubscriptionManager; import android.telephony.TelephonyFrameworkInitializer; import android.telephony.ims.aidl.IImsRcsController; import android.telephony.ims.aidl.IRcsUceControllerCallback; import android.telephony.ims.aidl.IRcsUcePublishStateCallback; +import android.telephony.ims.feature.RcsFeature; import android.util.Log; import java.lang.annotation.Retention; @@ -417,7 +420,7 @@ public class RcsUceAdapter { * <p> * After {@link CapabilitiesCallback#onComplete} or {@link CapabilitiesCallback#onError} has * been called, the reference to this callback will be discarded on the service side. - * @see #requestCapabilities(Executor, List, CapabilitiesCallback) + * @see #requestCapabilities(Collection, Executor, CapabilitiesCallback) * @hide */ @SystemApi @@ -464,10 +467,16 @@ public class RcsUceAdapter { } /** - * Request the User Capability Exchange capabilities for one or more contacts. + * Request the RCS capabilities for one or more contacts using RCS User Capability Exchange. * <p> - * This will return the cached capabilities of the contact and will not perform a capability - * poll on the network unless there are contacts being queried with stale information. + * This API will first check a local cache for the requested numbers and return the cached + * RCS capabilities of each number if the cache exists and is not stale. If the cache for a + * number is stale or there is no cached information about the requested number, the device will + * then perform a query to the carrier's network to request the RCS capabilities of the + * requested numbers. + * <p> + * Depending on the number of requests being sent, this API may throttled internally as the + * operations are queued to be executed by the carrier's network. * <p> * Be sure to check the availability of this feature using * {@link ImsRcsManager#isAvailable(int, int)} and ensuring @@ -552,13 +561,15 @@ public class RcsUceAdapter { } /** - * Ignore the device cache and perform a capability discovery for one contact, also called - * "availability fetch." + * Request the RCS capabilities for a phone number using User Capability Exchange. * <p> - * This will always perform a query to the network as long as requests are over the carrier - * availability fetch throttling threshold. If too many network requests are sent too quickly, - * #ERROR_TOO_MANY_REQUESTS will be returned. - * + * Unlike {@link #requestCapabilities(Collection, Executor, CapabilitiesCallback)}, which caches + * the result received from the network for a certain amount of time and uses that cached result + * for subsequent requests for RCS capabilities of the same phone number, this API will always + * request the RCS capabilities of a contact from the carrier's network. + * <p> + * Depending on the number of requests, this API may throttled internally as the operations are + * queued to be executed by the carrier's network. * <p> * Be sure to check the availability of this feature using * {@link ImsRcsManager#isAvailable(int, int)} and ensuring @@ -680,7 +691,8 @@ public class RcsUceAdapter { * state updates for the subscription specified in {@link ImsManager@getRcsManager(subid)}. * <p> * Use {@link SubscriptionManager.OnSubscriptionsChangedListener} to listen to subscription - * changed events and call {@link #unregisterPublishStateCallback} to clean up. + * changed events and call + * {@link #removeOnPublishStateChangedListener(OnPublishStateChangedListener)} to clean up. * <p> * The registered {@link OnPublishStateChangedListener} will also receive a callback when it is * registered with the current publish state. @@ -770,13 +782,23 @@ public class RcsUceAdapter { } /** - * The user’s setting for whether or not User Capability Exchange (UCE) is enabled for the - * associated subscription. + * The setting for whether or not the user has opted in to the automatic refresh of the RCS + * capabilities associated with the contacts in the user's contact address book. By default, + * this setting is disabled and must be enabled after the user has seen the opt-in dialog shown + * by {@link ImsRcsManager#ACTION_SHOW_CAPABILITY_DISCOVERY_OPT_IN}. + * <p> + * If this feature is enabled, the device will periodically share the phone numbers of all of + * the contacts in the user's address book with the carrier to refresh the RCS capabilities + * cache associated with those contacts as the local cache becomes stale. + * <p> + * This setting will only enable this feature if + * {@link CarrierConfigManager.Ims#KEY_RCS_BULK_CAPABILITY_EXCHANGE_BOOL} is also enabled. * <p> * Note: This setting does not affect whether or not the device publishes its service * capabilities if the subscription supports presence publication. * - * @return true if the user’s setting for UCE is enabled, false otherwise. + * @return true if the user has opted in for automatic refresh of the RCS capabilities of their + * contacts, false otherwise. * @throws ImsException if the subscription associated with this instance of * {@link RcsUceAdapter} is valid, but the ImsService associated with the subscription is not * available. This can happen if the ImsService has crashed, for example, or if the subscription @@ -802,18 +824,33 @@ public class RcsUceAdapter { } /** - * Change the user’s setting for whether or not UCE is enabled for the associated subscription. + * Change the user’s setting for whether or not the user has opted in to the automatic + * refresh of the RCS capabilities associated with the contacts in the user's contact address + * book. By default, this setting is disabled and must be enabled using this method after the + * user has seen the opt-in dialog shown by + * {@link ImsRcsManager#ACTION_SHOW_CAPABILITY_DISCOVERY_OPT_IN}. + * <p> + * If an application wishes to request that the user enable this feature, they must launch an + * Activity using the Intent {@link ImsRcsManager#ACTION_SHOW_CAPABILITY_DISCOVERY_OPT_IN}, + * which will ask the user if they wish to enable this feature. This setting must only be + * enabled after the user has opted-in to this feature. + * <p> + * This must not affect the + * {@link #requestCapabilities(Collection, Executor, CapabilitiesCallback)} or + * {@link #requestAvailability(Uri, Executor, CapabilitiesCallback)} API, + * as those APIs are still required for per-contact RCS capability queries of phone numbers + * required for operations such as placing a Video Telephony call or starting an RCS chat + * session. * <p> - * If an application Requires UCE, they will launch an Activity using the Intent - * {@link ImsRcsManager#ACTION_SHOW_CAPABILITY_DISCOVERY_OPT_IN}, which will ask the user if - * they wish to enable this feature. This setting should only be enabled after the user has - * opted-in to capability exchange. + * This setting will only enable this feature if + * {@link CarrierConfigManager.Ims#KEY_RCS_BULK_CAPABILITY_EXCHANGE_BOOL} is also enabled. * <p> * Note: This setting does not affect whether or not the device publishes its service * capabilities if the subscription supports presence publication. * - * @param isEnabled the user's setting for whether or not they wish for User - * Capability Exchange to be enabled. + * @param isEnabled true if the user has opted in for automatic refresh of the RCS capabilities + * of their contacts, or false if they have chosen to opt-out. By default this + * setting is disabled. * @throws ImsException if the subscription associated with this instance of * {@link RcsUceAdapter} is valid, but the ImsService associated with the subscription is not * available. This can happen if the ImsService has crashed, for example, or if the subscription diff --git a/telephony/java/com/android/internal/telephony/RILConstants.java b/telephony/java/com/android/internal/telephony/RILConstants.java index 3eda7482117f..822fc44146d6 100644 --- a/telephony/java/com/android/internal/telephony/RILConstants.java +++ b/telephony/java/com/android/internal/telephony/RILConstants.java @@ -497,6 +497,9 @@ public interface RILConstants { int RIL_REQUEST_ENABLE_MODEM = 146; int RIL_REQUEST_GET_MODEM_STATUS = 147; int RIL_REQUEST_CDMA_SEND_SMS_EXPECT_MORE = 148; + int RIL_REQUEST_GET_SIM_PHONEBOOK_CAPACITY = 149; + int RIL_REQUEST_GET_SIM_PHONEBOOK_RECORDS = 150; + int RIL_REQUEST_UPDATE_SIM_PHONEBOOK_RECORD = 151; /* The following requests are not defined in RIL.h */ int RIL_REQUEST_HAL_NON_RIL_BASE = 200; @@ -582,6 +585,8 @@ public interface RILConstants { int RIL_UNSOL_NETWORK_SCAN_RESULT = 1049; int RIL_UNSOL_KEEPALIVE_STATUS = 1050; int RIL_UNSOL_UNTHROTTLE_APN = 1052; + int RIL_UNSOL_RESPONSE_SIM_PHONEBOOK_CHANGED = 1053; + int RIL_UNSOL_RESPONSE_SIM_PHONEBOOK_RECORDS_RECEIVED = 1054; /* The following unsols are not defined in RIL.h */ int RIL_UNSOL_HAL_NON_RIL_BASE = 1100; diff --git a/tests/utils/testutils/java/com/android/internal/util/test/FsUtil.java b/tests/utils/testutils/java/com/android/internal/util/test/FsUtil.java new file mode 100644 index 000000000000..e65661298b7c --- /dev/null +++ b/tests/utils/testutils/java/com/android/internal/util/test/FsUtil.java @@ -0,0 +1,40 @@ +/* + * 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.internal.util.test; + +import java.io.File; + +public class FsUtil { + + /** + * Deletes all files under a given directory. Deliberately ignores errors, on the assumption + * that test cleanup is only supposed to be best-effort. + * + * @param dir directory to clear its contents + */ + public static void deleteContents(File dir) { + File[] files = dir.listFiles(); + if (files != null) { + for (File file : files) { + if (file.isDirectory()) { + deleteContents(file); + } + file.delete(); + } + } + } +} diff --git a/tools/aapt2/OWNERS b/tools/aapt2/OWNERS index f1903a5a54a7..69dfcc98340d 100644 --- a/tools/aapt2/OWNERS +++ b/tools/aapt2/OWNERS @@ -1,3 +1,4 @@ set noparent toddke@google.com -rtmitchell@google.com
\ No newline at end of file +rtmitchell@google.com +patb@google.com
\ No newline at end of file diff --git a/tools/aosp/aosp_sha.sh b/tools/aosp/aosp_sha.sh index f25fcdcb7479..514f17a042bc 100755 --- a/tools/aosp/aosp_sha.sh +++ b/tools/aosp/aosp_sha.sh @@ -5,7 +5,21 @@ if git branch -vv | grep -q -P "^\*[^\[]+\[aosp/"; then # Change appears to be in AOSP exit 0 else - # Change appears to be non-AOSP; search for files + # Change appears to be non-AOSP. + + # If this is a cherry-pick, then allow it. + cherrypick=0 + while read -r line ; do + if [[ $line =~ cherry\ picked\ from ]] ; then + (( cherrypick++ )) + fi + done < <(git show $1) + if (( cherrypick != 0 )); then + # This is a cherry-pick, so allow it. + exit 0 + fi + + # See if any files are affected. count=0 while read -r file ; do if (( count == 0 )); then diff --git a/tools/codegen/src/com/android/codegen/Utils.kt b/tools/codegen/src/com/android/codegen/Utils.kt index c19ae3b0b11f..a117aa09ab62 100644 --- a/tools/codegen/src/com/android/codegen/Utils.kt +++ b/tools/codegen/src/com/android/codegen/Utils.kt @@ -43,8 +43,8 @@ inline infix fun Int.times(action: () -> Unit) { * cccc dd */ fun Iterable<Pair<String, String>>.columnize(separator: String = " | "): String { - val col1w = map { (a, _) -> a.length }.max()!! - val col2w = map { (_, b) -> b.length }.max()!! + val col1w = map { (a, _) -> a.length }.maxOrNull()!! + val col2w = map { (_, b) -> b.length }.maxOrNull()!! return map { it.first.padEnd(col1w) + separator + it.second.padEnd(col2w) }.joinToString("\n") } |