summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/api/current.txt1
-rw-r--r--core/api/system-current.txt12
-rw-r--r--core/java/android/hardware/soundtrigger/OWNERS3
-rw-r--r--core/java/android/net/NetworkIdentity.java57
-rw-r--r--core/java/android/net/NetworkTemplate.java48
-rw-r--r--core/java/android/os/ZygoteProcess.java2
-rw-r--r--core/java/android/uwb/AngleOfArrivalSupport.aidl44
-rw-r--r--core/java/android/uwb/IUwbAdapter.aidl59
-rw-r--r--core/java/android/uwb/UwbManager.java175
-rw-r--r--core/java/com/android/internal/os/Zygote.java360
-rw-r--r--core/java/com/android/internal/os/ZygoteArguments.java83
-rw-r--r--core/java/com/android/internal/os/ZygoteCommandBuffer.java187
-rw-r--r--core/java/com/android/internal/os/ZygoteConnection.java329
-rw-r--r--core/java/com/android/internal/os/ZygoteConnectionConstants.java3
-rw-r--r--core/java/com/android/internal/os/ZygoteInit.java11
-rw-r--r--core/java/com/android/internal/os/ZygoteServer.java14
-rw-r--r--core/java/com/android/server/OWNERS1
-rw-r--r--core/jni/Android.bp1
-rw-r--r--core/jni/AndroidRuntime.cpp2
-rw-r--r--core/jni/OWNERS3
-rw-r--r--core/jni/com_android_internal_os_Zygote.cpp371
-rw-r--r--core/jni/com_android_internal_os_Zygote.h78
-rw-r--r--core/jni/com_android_internal_os_ZygoteCommandBuffer.cpp512
-rw-r--r--core/proto/android/service/netstats.proto2
-rw-r--r--keystore/java/android/security/AndroidKeyStoreMaintenance.java25
-rw-r--r--keystore/java/android/security/KeyStore.java4
-rw-r--r--keystore/java/android/security/KeyStore2.java45
-rw-r--r--libs/hwui/RecordingCanvas.cpp21
-rw-r--r--libs/hwui/hwui/Canvas.cpp4
-rw-r--r--libs/hwui/hwui/Canvas.h8
-rw-r--r--media/java/android/media/soundtrigger/OWNERS1
-rw-r--r--packages/Connectivity/framework/src/android/net/NetworkRequest.java69
-rw-r--r--packages/Connectivity/framework/src/android/net/NetworkUtils.java3
-rw-r--r--packages/Connectivity/framework/src/android/net/VpnTransportInfo.java2
-rw-r--r--packages/Connectivity/framework/src/android/net/util/MultinetworkPolicyTracker.java38
-rw-r--r--services/core/java/com/android/server/ConnectivityService.java75
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerService.java5
-rw-r--r--services/core/java/com/android/server/connectivity/MultipathPolicyTracker.java6
-rw-r--r--services/core/java/com/android/server/locksettings/OWNERS1
-rw-r--r--services/core/java/com/android/server/locksettings/RebootEscrowProviderServerBasedImpl.java2
-rw-r--r--services/core/java/com/android/server/net/NetworkIdentitySet.java13
-rw-r--r--services/core/java/com/android/server/net/NetworkPolicyManagerService.java15
-rw-r--r--services/core/java/com/android/server/net/NetworkStatsService.java2
-rw-r--r--services/core/java/com/android/server/pm/PackageDexOptimizer.java3
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerService.java34
-rw-r--r--services/core/java/com/android/server/pm/dex/ArtPackageInfo.java58
-rw-r--r--services/core/java/com/android/server/pm/dex/ArtUtils.java64
-rw-r--r--services/core/java/com/android/server/pm/dex/DexManager.java16
-rw-r--r--services/tests/mockingservicestests/src/com/android/server/job/controllers/ConnectivityControllerTest.java2
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/OWNERS2
-rw-r--r--services/tests/servicestests/src/com/android/server/soundtrigger_middleware/OWNERS2
-rw-r--r--telephony/java/android/telephony/RadioInterfaceCapabilities.java53
-rw-r--r--telephony/java/android/telephony/SignalStrengthUpdateRequest.java2
-rw-r--r--telephony/java/android/telephony/TelephonyManager.java53
-rw-r--r--tests/net/common/java/android/net/CaptivePortalDataTest.kt50
-rw-r--r--tests/net/integration/util/com/android/server/NetworkAgentWrapper.java2
-rw-r--r--tests/net/java/android/net/NetworkIdentityTest.kt54
-rw-r--r--tests/net/java/android/net/NetworkTemplateTest.kt107
-rw-r--r--tests/net/java/android/net/VpnTransportInfoTest.java2
-rw-r--r--tests/net/java/android/net/util/MultinetworkPolicyTrackerTest.kt137
-rw-r--r--tests/net/java/com/android/server/ConnectivityServiceTest.java19
-rw-r--r--tests/net/java/com/android/server/net/NetworkStatsCollectionTest.java5
-rw-r--r--tests/net/java/com/android/server/net/NetworkStatsObserversTest.java3
-rw-r--r--tests/net/java/com/android/server/net/NetworkStatsServiceTest.java130
64 files changed, 2471 insertions, 1024 deletions
diff --git a/core/api/current.txt b/core/api/current.txt
index bc0272fc4432..80c555bd15b9 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -40972,7 +40972,6 @@ package android.telephony {
public final class SignalStrengthUpdateRequest implements android.os.Parcelable {
method public int describeContents();
- method @NonNull public android.os.IBinder getLiveToken();
method @NonNull public java.util.Collection<android.telephony.SignalThresholdInfo> getSignalThresholdInfos();
method public boolean isReportingRequestedWhileIdle();
method public void writeToParcel(@NonNull android.os.Parcel, int);
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 42488259cf27..b15544775a8b 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -10607,6 +10607,7 @@ package android.telephony {
field public static final int CALL_WAITING_STATUS_ENABLED = 1; // 0x1
field public static final int CALL_WAITING_STATUS_NOT_SUPPORTED = 4; // 0x4
field public static final int CALL_WAITING_STATUS_UNKNOWN_ERROR = 3; // 0x3
+ field public static final String CAPABILITY_ALLOWED_NETWORK_TYPES_USED = "CAPABILITY_ALLOWED_NETWORK_TYPES_USED";
field public static final String CAPABILITY_SECONDARY_LINK_BANDWIDTH_VISIBLE = "CAPABILITY_SECONDARY_LINK_BANDWIDTH_VISIBLE";
field public static final int CARRIER_PRIVILEGE_STATUS_ERROR_LOADING_RULES = -2; // 0xfffffffe
field public static final int CARRIER_PRIVILEGE_STATUS_HAS_ACCESS = 1; // 0x1
@@ -12779,21 +12780,10 @@ package android.uwb {
public final class UwbManager {
method @RequiresPermission(android.Manifest.permission.UWB_PRIVILEGED) public long elapsedRealtimeResolutionNanos();
- method @RequiresPermission(android.Manifest.permission.UWB_PRIVILEGED) public int getAngleOfArrivalSupport();
- method @RequiresPermission(android.Manifest.permission.UWB_PRIVILEGED) public int getMaxRemoteDevicesPerInitiatorSession();
- method @RequiresPermission(android.Manifest.permission.UWB_PRIVILEGED) public int getMaxRemoteDevicesPerResponderSession();
- method @RequiresPermission(android.Manifest.permission.UWB_PRIVILEGED) public int getMaxSimultaneousSessions();
method @NonNull @RequiresPermission(android.Manifest.permission.UWB_PRIVILEGED) public android.os.PersistableBundle getSpecificationInfo();
- method @NonNull @RequiresPermission(android.Manifest.permission.UWB_PRIVILEGED) public java.util.List<java.lang.Integer> getSupportedChannelNumbers();
- method @NonNull @RequiresPermission(android.Manifest.permission.UWB_PRIVILEGED) public java.util.Set<java.lang.Integer> getSupportedPreambleCodeIndices();
- method @RequiresPermission(android.Manifest.permission.UWB_PRIVILEGED) public boolean isRangingSupported();
method @NonNull @RequiresPermission(android.Manifest.permission.UWB_PRIVILEGED) public AutoCloseable openRangingSession(@NonNull android.os.PersistableBundle, @NonNull java.util.concurrent.Executor, @NonNull android.uwb.RangingSession.Callback);
method @RequiresPermission(android.Manifest.permission.UWB_PRIVILEGED) public void registerAdapterStateCallback(@NonNull java.util.concurrent.Executor, @NonNull android.uwb.UwbManager.AdapterStateCallback);
method @RequiresPermission(android.Manifest.permission.UWB_PRIVILEGED) public void unregisterAdapterStateCallback(@NonNull android.uwb.UwbManager.AdapterStateCallback);
- field public static final int ANGLE_OF_ARRIVAL_SUPPORT_TYPE_2D = 2; // 0x2
- field public static final int ANGLE_OF_ARRIVAL_SUPPORT_TYPE_3D_HEMISPHERICAL = 3; // 0x3
- field public static final int ANGLE_OF_ARRIVAL_SUPPORT_TYPE_3D_SPHERICAL = 4; // 0x4
- field public static final int ANGLE_OF_ARRIVAL_SUPPORT_TYPE_NONE = 1; // 0x1
}
public static interface UwbManager.AdapterStateCallback {
diff --git a/core/java/android/hardware/soundtrigger/OWNERS b/core/java/android/hardware/soundtrigger/OWNERS
index 816bc6bba639..e5d037003ac4 100644
--- a/core/java/android/hardware/soundtrigger/OWNERS
+++ b/core/java/android/hardware/soundtrigger/OWNERS
@@ -1 +1,2 @@
-include /core/java/android/media/soundtrigger/OWNERS
+ytai@google.com
+elaurent@google.com
diff --git a/core/java/android/net/NetworkIdentity.java b/core/java/android/net/NetworkIdentity.java
index 32b19a462218..303a40755d4e 100644
--- a/core/java/android/net/NetworkIdentity.java
+++ b/core/java/android/net/NetworkIdentity.java
@@ -18,6 +18,7 @@ package android.net;
import static android.net.ConnectivityManager.TYPE_WIFI;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
import android.net.wifi.WifiInfo;
@@ -41,6 +42,22 @@ public class NetworkIdentity implements Comparable<NetworkIdentity> {
public static final int SUBTYPE_COMBINED = -1;
+ /**
+ * Network has no {@code NetworkCapabilities#NET_CAPABILITY_OEM_*}.
+ * @hide
+ */
+ public static final int OEM_NONE = 0x0;
+ /**
+ * Network has {@link NetworkCapabilities#NET_CAPABILITY_OEM_PAID}.
+ * @hide
+ */
+ public static final int OEM_PAID = 0x1;
+ /**
+ * Network has {@link NetworkCapabilities#NET_CAPABILITY_OEM_PRIVATE}.
+ * @hide
+ */
+ public static final int OEM_PRIVATE = 0x2;
+
final int mType;
final int mSubType;
final String mSubscriberId;
@@ -48,10 +65,11 @@ public class NetworkIdentity implements Comparable<NetworkIdentity> {
final boolean mRoaming;
final boolean mMetered;
final boolean mDefaultNetwork;
+ final int mOemManaged;
public NetworkIdentity(
int type, int subType, String subscriberId, String networkId, boolean roaming,
- boolean metered, boolean defaultNetwork) {
+ boolean metered, boolean defaultNetwork, int oemManaged) {
mType = type;
mSubType = subType;
mSubscriberId = subscriberId;
@@ -59,12 +77,13 @@ public class NetworkIdentity implements Comparable<NetworkIdentity> {
mRoaming = roaming;
mMetered = metered;
mDefaultNetwork = defaultNetwork;
+ mOemManaged = oemManaged;
}
@Override
public int hashCode() {
return Objects.hash(mType, mSubType, mSubscriberId, mNetworkId, mRoaming, mMetered,
- mDefaultNetwork);
+ mDefaultNetwork, mOemManaged);
}
@Override
@@ -75,7 +94,8 @@ public class NetworkIdentity implements Comparable<NetworkIdentity> {
&& Objects.equals(mSubscriberId, ident.mSubscriberId)
&& Objects.equals(mNetworkId, ident.mNetworkId)
&& mMetered == ident.mMetered
- && mDefaultNetwork == ident.mDefaultNetwork;
+ && mDefaultNetwork == ident.mDefaultNetwork
+ && mOemManaged == ident.mOemManaged;
}
return false;
}
@@ -102,6 +122,8 @@ 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);
return builder.append("}").toString();
}
@@ -120,6 +142,7 @@ public class NetworkIdentity implements Comparable<NetworkIdentity> {
proto.write(NetworkIdentityProto.ROAMING, mRoaming);
proto.write(NetworkIdentityProto.METERED, mMetered);
proto.write(NetworkIdentityProto.DEFAULT_NETWORK, mDefaultNetwork);
+ proto.write(NetworkIdentityProto.OEM_MANAGED_NETWORK, mOemManaged);
proto.end(start);
}
@@ -152,6 +175,10 @@ public class NetworkIdentity implements Comparable<NetworkIdentity> {
return mDefaultNetwork;
}
+ public int getOemManaged() {
+ return mOemManaged;
+ }
+
/**
* Build a {@link NetworkIdentity} from the given {@link NetworkState} and {@code subType},
* assuming that any mobile networks are using the current IMSI. The subType if applicable,
@@ -171,6 +198,8 @@ public class NetworkIdentity implements Comparable<NetworkIdentity> {
subscriberId = state.subscriberId;
+ final int oemManaged = getOemBitfield(state.networkCapabilities);
+
if (legacyType == TYPE_WIFI) {
if (state.networkCapabilities.getSsid() != null) {
networkId = state.networkCapabilities.getSsid();
@@ -185,7 +214,24 @@ public class NetworkIdentity implements Comparable<NetworkIdentity> {
}
return new NetworkIdentity(legacyType, subType, subscriberId, networkId, roaming, metered,
- defaultNetwork);
+ defaultNetwork, oemManaged);
+ }
+
+ /**
+ * Builds a bitfield of {@code NetworkIdentity.OEM_*} based on {@link NetworkCapabilities}.
+ * @hide
+ */
+ public static int getOemBitfield(NetworkCapabilities nc) {
+ int oemManaged = OEM_NONE;
+
+ if (nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_OEM_PAID)) {
+ oemManaged |= OEM_PAID;
+ }
+ if (nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_OEM_PRIVATE)) {
+ oemManaged |= OEM_PRIVATE;
+ }
+
+ return oemManaged;
}
@Override
@@ -209,6 +255,9 @@ public class NetworkIdentity implements Comparable<NetworkIdentity> {
if (res == 0) {
res = Boolean.compare(mDefaultNetwork, another.mDefaultNetwork);
}
+ if (res == 0) {
+ res = Integer.compare(mOemManaged, another.mOemManaged);
+ }
return res;
}
}
diff --git a/core/java/android/net/NetworkTemplate.java b/core/java/android/net/NetworkTemplate.java
index aa61e03b285c..c83dd99c2a3b 100644
--- a/core/java/android/net/NetworkTemplate.java
+++ b/core/java/android/net/NetworkTemplate.java
@@ -23,6 +23,7 @@ import static android.net.ConnectivityManager.TYPE_PROXY;
import static android.net.ConnectivityManager.TYPE_WIFI;
import static android.net.ConnectivityManager.TYPE_WIFI_P2P;
import static android.net.ConnectivityManager.TYPE_WIMAX;
+import static android.net.NetworkIdentity.OEM_NONE;
import static android.net.NetworkStats.DEFAULT_NETWORK_ALL;
import static android.net.NetworkStats.DEFAULT_NETWORK_NO;
import static android.net.NetworkStats.DEFAULT_NETWORK_YES;
@@ -99,6 +100,22 @@ public class NetworkTemplate implements Parcelable {
*/
public static final int NETWORK_TYPE_5G_NSA = -2;
+ /**
+ * Value to match both OEM managed and unmanaged networks (all networks).
+ * @hide
+ */
+ public static final int OEM_MANAGED_ALL = -1;
+ /**
+ * Value to match networks which are not OEM managed.
+ * @hide
+ */
+ public static final int OEM_MANAGED_NO = OEM_NONE;
+ /**
+ * Value to match any OEM managed network.
+ * @hide
+ */
+ public static final int OEM_MANAGED_YES = -2;
+
private static boolean isKnownMatchRule(final int rule) {
switch (rule) {
case MATCH_MOBILE:
@@ -151,10 +168,10 @@ public class NetworkTemplate implements Parcelable {
@NetworkType int ratType) {
if (TextUtils.isEmpty(subscriberId)) {
return new NetworkTemplate(MATCH_MOBILE_WILDCARD, null, null, null,
- METERED_ALL, ROAMING_ALL, DEFAULT_NETWORK_ALL, ratType);
+ METERED_ALL, ROAMING_ALL, DEFAULT_NETWORK_ALL, ratType, OEM_MANAGED_ALL);
}
return new NetworkTemplate(MATCH_MOBILE, subscriberId, new String[]{subscriberId}, null,
- METERED_ALL, ROAMING_ALL, DEFAULT_NETWORK_ALL, ratType);
+ METERED_ALL, ROAMING_ALL, DEFAULT_NETWORK_ALL, ratType, OEM_MANAGED_ALL);
}
/**
@@ -235,6 +252,9 @@ public class NetworkTemplate implements Parcelable {
private final int mDefaultNetwork;
private final int mSubType;
+ // Bitfield containing OEM network properties{@code NetworkIdentity#OEM_*}.
+ private final int mOemManaged;
+
@UnsupportedAppUsage
public NetworkTemplate(int matchRule, String subscriberId, String networkId) {
this(matchRule, subscriberId, new String[] { subscriberId }, networkId);
@@ -243,11 +263,12 @@ public class NetworkTemplate implements Parcelable {
public NetworkTemplate(int matchRule, String subscriberId, String[] matchSubscriberIds,
String networkId) {
this(matchRule, subscriberId, matchSubscriberIds, networkId, METERED_ALL, ROAMING_ALL,
- DEFAULT_NETWORK_ALL, NETWORK_TYPE_ALL);
+ DEFAULT_NETWORK_ALL, NETWORK_TYPE_ALL, OEM_MANAGED_ALL);
}
public NetworkTemplate(int matchRule, String subscriberId, String[] matchSubscriberIds,
- String networkId, int metered, int roaming, int defaultNetwork, int subType) {
+ String networkId, int metered, int roaming, int defaultNetwork, int subType,
+ int oemManaged) {
mMatchRule = matchRule;
mSubscriberId = subscriberId;
mMatchSubscriberIds = matchSubscriberIds;
@@ -256,6 +277,7 @@ public class NetworkTemplate implements Parcelable {
mRoaming = roaming;
mDefaultNetwork = defaultNetwork;
mSubType = subType;
+ mOemManaged = oemManaged;
if (!isKnownMatchRule(matchRule)) {
Log.e(TAG, "Unknown network template rule " + matchRule
@@ -272,6 +294,7 @@ public class NetworkTemplate implements Parcelable {
mRoaming = in.readInt();
mDefaultNetwork = in.readInt();
mSubType = in.readInt();
+ mOemManaged = in.readInt();
}
@Override
@@ -284,6 +307,7 @@ public class NetworkTemplate implements Parcelable {
dest.writeInt(mRoaming);
dest.writeInt(mDefaultNetwork);
dest.writeInt(mSubType);
+ dest.writeInt(mOemManaged);
}
@Override
@@ -319,13 +343,16 @@ public class NetworkTemplate implements Parcelable {
if (mSubType != NETWORK_TYPE_ALL) {
builder.append(", subType=").append(mSubType);
}
+ if (mOemManaged != OEM_MANAGED_ALL) {
+ builder.append(", oemManaged=").append(mOemManaged);
+ }
return builder.toString();
}
@Override
public int hashCode() {
return Objects.hash(mMatchRule, mSubscriberId, mNetworkId, mMetered, mRoaming,
- mDefaultNetwork, mSubType);
+ mDefaultNetwork, mSubType, mOemManaged);
}
@Override
@@ -338,7 +365,8 @@ public class NetworkTemplate implements Parcelable {
&& mMetered == other.mMetered
&& mRoaming == other.mRoaming
&& mDefaultNetwork == other.mDefaultNetwork
- && mSubType == other.mSubType;
+ && mSubType == other.mSubType
+ && mOemManaged == other.mOemManaged;
}
return false;
}
@@ -384,6 +412,7 @@ public class NetworkTemplate implements Parcelable {
if (!matchesMetered(ident)) return false;
if (!matchesRoaming(ident)) return false;
if (!matchesDefaultNetwork(ident)) return false;
+ if (!matchesOemNetwork(ident)) return false;
switch (mMatchRule) {
case MATCH_MOBILE:
@@ -425,6 +454,13 @@ public class NetworkTemplate implements Parcelable {
|| (mDefaultNetwork == DEFAULT_NETWORK_NO && !ident.mDefaultNetwork);
}
+ private boolean matchesOemNetwork(NetworkIdentity ident) {
+ return (mOemManaged == OEM_MANAGED_ALL)
+ || (mOemManaged == OEM_MANAGED_YES
+ && ident.mOemManaged != OEM_NONE)
+ || (mOemManaged == ident.mOemManaged);
+ }
+
private boolean matchesCollapsedRatType(NetworkIdentity ident) {
return mSubType == NETWORK_TYPE_ALL
|| getCollapsedRatType(mSubType) == getCollapsedRatType(ident.mSubType);
diff --git a/core/java/android/os/ZygoteProcess.java b/core/java/android/os/ZygoteProcess.java
index 9e332e9b0456..c9da6418471d 100644
--- a/core/java/android/os/ZygoteProcess.java
+++ b/core/java/android/os/ZygoteProcess.java
@@ -426,7 +426,7 @@ public class ZygoteProcess {
// avoid writing a partial response to the zygote.
for (String arg : args) {
// Making two indexOf calls here is faster than running a manually fused loop due
- // to the fact that indexOf is a optimized intrinsic.
+ // to the fact that indexOf is an optimized intrinsic.
if (arg.indexOf('\n') >= 0) {
throw new ZygoteStartFailedEx("Embedded newlines not allowed");
} else if (arg.indexOf('\r') >= 0) {
diff --git a/core/java/android/uwb/AngleOfArrivalSupport.aidl b/core/java/android/uwb/AngleOfArrivalSupport.aidl
deleted file mode 100644
index 57666ff8bca9..000000000000
--- a/core/java/android/uwb/AngleOfArrivalSupport.aidl
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * Copyright 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 android.uwb;
-
-/**
- * @hide
- */
-@Backing(type="int")
-enum AngleOfArrivalSupport {
- /**
- * The device does not support angle of arrival
- */
- NONE,
-
- /**
- * The device supports planar angle of arrival
- */
- TWO_DIMENSIONAL,
-
- /**
- * The device does supports three dimensional angle of arrival with hemispherical azimuth angles
- */
- THREE_DIMENSIONAL_HEMISPHERICAL,
-
- /**
- * The device does supports three dimensional angle of arrival with full azimuth angles
- */
- THREE_DIMENSIONAL_SPHERICAL,
-}
-
diff --git a/core/java/android/uwb/IUwbAdapter.aidl b/core/java/android/uwb/IUwbAdapter.aidl
index b9c55081a103..468a69c7bddb 100644
--- a/core/java/android/uwb/IUwbAdapter.aidl
+++ b/core/java/android/uwb/IUwbAdapter.aidl
@@ -17,7 +17,6 @@
package android.uwb;
import android.os.PersistableBundle;
-import android.uwb.AngleOfArrivalSupport;
import android.uwb.IUwbAdapterStateCallbacks;
import android.uwb.IUwbRangingCallbacks;
import android.uwb.SessionHandle;
@@ -47,43 +46,6 @@ interface IUwbAdapter {
void unregisterAdapterStateCallbacks(in IUwbAdapterStateCallbacks callbacks);
/**
- * Returns true if ranging is supported, false otherwise
- */
- boolean isRangingSupported();
-
- /**
- * Get the angle of arrival supported by this device
- *
- * @return the angle of arrival type supported
- */
- AngleOfArrivalSupport getAngleOfArrivalSupport();
-
- /**
- * Generates a list of the supported 802.15.4z channels
- *
- * The list must be prioritized in the order of preferred channel usage.
- *
- * The list must only contain channels that are permitted to be used in the
- * device's current location.
- *
- * @return an array of support channels on the device for the current location.
- */
- int[] getSupportedChannels();
-
- /**
- * Generates a list of the supported 802.15.4z preamble codes
- *
- * The list must be prioritized in the order of preferred preamble usage.
- *
- * The list must only contain preambles that are permitted to be used in the
- * device's current location.
- *
- * @return an array of supported preambles on the device for the current
- * location.
- */
- int[] getSupportedPreambleCodes();
-
- /**
* Get the accuracy of the ranging timestamps
*
* @return accuracy of the ranging timestamps in nanoseconds
@@ -91,27 +53,6 @@ interface IUwbAdapter {
long getTimestampResolutionNanos();
/**
- * Get the supported number of simultaneous ranging sessions
- *
- * @return the supported number of simultaneous ranging sessions
- */
- int getMaxSimultaneousSessions();
-
- /**
- * Get the maximum number of remote devices per session when local device is initiator
- *
- * @return the maximum number of remote devices supported in a single session
- */
- int getMaxRemoteDevicesPerInitiatorSession();
-
- /**
- * Get the maximum number of remote devices per session when local device is responder
- *
- * @return the maximum number of remote devices supported in a single session
- */
- int getMaxRemoteDevicesPerResponderSession();
-
- /**
* Provides the capabilities and features of the device
*
* @return specification specific capabilities and features of the device
diff --git a/core/java/android/uwb/UwbManager.java b/core/java/android/uwb/UwbManager.java
index 2dc0ba0b9b80..63a6d058f358 100644
--- a/core/java/android/uwb/UwbManager.java
+++ b/core/java/android/uwb/UwbManager.java
@@ -32,10 +32,6 @@ import android.os.ServiceManager;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
-import java.util.ArrayList;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
import java.util.concurrent.Executor;
/**
@@ -195,133 +191,6 @@ public final class UwbManager {
}
/**
- * Check if ranging is supported, regardless of ranging method
- *
- * @return true if ranging is supported
- */
- @RequiresPermission(Manifest.permission.UWB_PRIVILEGED)
- public boolean isRangingSupported() {
- try {
- return mUwbAdapter.isRangingSupported();
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
-
- /**
- * @hide
- */
- @Retention(RetentionPolicy.SOURCE)
- @IntDef(value = {
- ANGLE_OF_ARRIVAL_SUPPORT_TYPE_NONE,
- ANGLE_OF_ARRIVAL_SUPPORT_TYPE_2D,
- ANGLE_OF_ARRIVAL_SUPPORT_TYPE_3D_HEMISPHERICAL,
- ANGLE_OF_ARRIVAL_SUPPORT_TYPE_3D_SPHERICAL})
- public @interface AngleOfArrivalSupportType {}
-
- /**
- * Indicate absence of support for angle of arrival measurement
- */
- public static final int ANGLE_OF_ARRIVAL_SUPPORT_TYPE_NONE = 1;
-
- /**
- * Indicate support for planar angle of arrival measurement, due to antenna
- * limitation. Typically requires at least two antennas.
- */
- public static final int ANGLE_OF_ARRIVAL_SUPPORT_TYPE_2D = 2;
-
- /**
- * Indicate support for three dimensional angle of arrival measurement.
- * Typically requires at least three antennas. However, due to antenna
- * arrangement, a platform may only support hemi-spherical azimuth angles
- * ranging from -pi/2 to pi/2
- */
- public static final int ANGLE_OF_ARRIVAL_SUPPORT_TYPE_3D_HEMISPHERICAL = 3;
-
- /**
- * Indicate support for three dimensional angle of arrival measurement.
- * Typically requires at least three antennas. This mode supports full
- * azimuth angles ranging from -pi to pi.
- */
- public static final int ANGLE_OF_ARRIVAL_SUPPORT_TYPE_3D_SPHERICAL = 4;
-
- /**
- * Gets the {@link AngleOfArrivalSupportType} supported on this platform
- * <p>Possible return values are
- * {@link #ANGLE_OF_ARRIVAL_SUPPORT_TYPE_NONE},
- * {@link #ANGLE_OF_ARRIVAL_SUPPORT_TYPE_2D},
- * {@link #ANGLE_OF_ARRIVAL_SUPPORT_TYPE_3D_HEMISPHERICAL},
- * {@link #ANGLE_OF_ARRIVAL_SUPPORT_TYPE_3D_SPHERICAL}.
- *
- * @return angle of arrival type supported
- */
- @AngleOfArrivalSupportType
- @RequiresPermission(Manifest.permission.UWB_PRIVILEGED)
- public int getAngleOfArrivalSupport() {
- try {
- switch (mUwbAdapter.getAngleOfArrivalSupport()) {
- case AngleOfArrivalSupport.TWO_DIMENSIONAL:
- return ANGLE_OF_ARRIVAL_SUPPORT_TYPE_2D;
-
- case AngleOfArrivalSupport.THREE_DIMENSIONAL_HEMISPHERICAL:
- return ANGLE_OF_ARRIVAL_SUPPORT_TYPE_3D_HEMISPHERICAL;
-
- case AngleOfArrivalSupport.THREE_DIMENSIONAL_SPHERICAL:
- return ANGLE_OF_ARRIVAL_SUPPORT_TYPE_3D_SPHERICAL;
-
- case AngleOfArrivalSupport.NONE:
- default:
- return ANGLE_OF_ARRIVAL_SUPPORT_TYPE_NONE;
- }
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
-
- /**
- * Get a {@link List} of supported channel numbers based on the device's current location
- * <p>The returned values are ordered by the system's desired ordered of use, with the first
- * entry being the most preferred.
- *
- * <p>Channel numbers are defined based on the IEEE 802.15.4z standard for UWB.
- *
- * @return {@link List} of supported channel numbers ordered by preference
- */
- @NonNull
- @RequiresPermission(Manifest.permission.UWB_PRIVILEGED)
- public List<Integer> getSupportedChannelNumbers() {
- List<Integer> channels = new ArrayList<>();
- try {
- for (int channel : mUwbAdapter.getSupportedChannels()) {
- channels.add(channel);
- }
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- return channels;
- }
-
- /**
- * Get a {@link List} of supported preamble code indices
- * <p> Preamble code indices are defined based on the IEEE 802.15.4z standard for UWB.
- *
- * @return {@link List} of supported preamble code indices
- */
- @NonNull
- @RequiresPermission(Manifest.permission.UWB_PRIVILEGED)
- public Set<Integer> getSupportedPreambleCodeIndices() {
- Set<Integer> preambles = new HashSet<>();
- try {
- for (int preamble : mUwbAdapter.getSupportedPreambleCodes()) {
- preambles.add(preamble);
- }
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- return preambles;
- }
-
- /**
* Get the timestamp resolution for events in nanoseconds
* <p>This value defines the maximum error of all timestamps for events reported to
* {@link RangingSession.Callback}.
@@ -339,50 +208,6 @@ public final class UwbManager {
}
/**
- * Get the number of simultaneous sessions allowed in the system
- *
- * @return the maximum allowed number of simultaneously open {@link RangingSession} instances.
- */
- @RequiresPermission(Manifest.permission.UWB_PRIVILEGED)
- public int getMaxSimultaneousSessions() {
- try {
- return mUwbAdapter.getMaxSimultaneousSessions();
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
-
- /**
- * Get the maximum number of remote devices in a {@link RangingSession} when the local device
- * is the initiator.
- *
- * @return the maximum number of remote devices per {@link RangingSession}
- */
- @RequiresPermission(Manifest.permission.UWB_PRIVILEGED)
- public int getMaxRemoteDevicesPerInitiatorSession() {
- try {
- return mUwbAdapter.getMaxRemoteDevicesPerInitiatorSession();
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
-
- /**
- * Get the maximum number of remote devices in a {@link RangingSession} when the local device
- * is a responder.
- *
- * @return the maximum number of remote devices per {@link RangingSession}
- */
- @RequiresPermission(Manifest.permission.UWB_PRIVILEGED)
- public int getMaxRemoteDevicesPerResponderSession() {
- try {
- return mUwbAdapter.getMaxRemoteDevicesPerResponderSession();
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
-
- /**
* Open a {@link RangingSession} with the given parameters
* <p>The {@link RangingSession.Callback#onOpened(RangingSession)} function is called with a
* {@link RangingSession} object used to control ranging when the session is successfully
diff --git a/core/java/com/android/internal/os/Zygote.java b/core/java/com/android/internal/os/Zygote.java
index 408e591b6336..9abc55b983a0 100644
--- a/core/java/com/android/internal/os/Zygote.java
+++ b/core/java/com/android/internal/os/Zygote.java
@@ -18,8 +18,8 @@ package com.android.internal.os;
import static android.system.OsConstants.O_CLOEXEC;
-import static com.android.internal.os.ZygoteConnectionConstants.MAX_ZYGOTE_ARGC;
-
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.content.pm.ApplicationInfo;
import android.net.Credentials;
import android.net.LocalServerSocket;
@@ -36,17 +36,16 @@ import android.util.Log;
import com.android.internal.net.NetworkUtilsInternal;
+import dalvik.annotation.optimization.CriticalNative;
import dalvik.annotation.optimization.FastNative;
import dalvik.system.ZygoteHooks;
import libcore.io.IoUtils;
-import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.FileDescriptor;
import java.io.IOException;
-import java.io.InputStreamReader;
/** @hide */
public final class Zygote {
@@ -243,6 +242,8 @@ public final class Zygote {
*/
public static final String CHILD_ZYGOTE_UID_RANGE_END = "--uid-range-end=";
+ private static final String TAG = "Zygote";
+
/** Prefix prepended to socket names created by init */
private static final String ANDROID_SOCKET_PREFIX = "ANDROID_SOCKET_";
@@ -413,6 +414,10 @@ public final class Zygote {
// Note that this event ends at the end of handleChildProc.
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "PostFork");
+ if (gids != null && gids.length > 0) {
+ NetworkUtilsInternal.setAllowNetworkingForProcess(containsInetGid(gids));
+ }
+
// Set the Java Language thread priority to the default value for new apps.
Thread.currentThread().setPriority(Thread.NORM_PRIORITY);
@@ -585,114 +590,163 @@ public final class Zygote {
private static native int nativeGetUsapPoolEventFD();
/**
- * Fork a new unspecialized app process from the zygote
+ * Fork a new unspecialized app process from the zygote. Adds the Usap to the native
+ * Usap table.
*
* @param usapPoolSocket The server socket the USAP will call accept on
- * @param sessionSocketRawFDs Anonymous session sockets that are currently open
- * @param isPriorityFork Value controlling the process priority level until accept is called
- * @return In the Zygote process this function will always return null; in unspecialized app
- * processes this function will return a Runnable object representing the new
- * application that is passed up from usapMain.
+ * @param sessionSocketRawFDs Anonymous session sockets that are currently open.
+ * These are closed in the child.
+ * @param isPriorityFork Raise the initial process priority level because this is on the
+ * critical path for application startup.
+ * @return In the child process, this returns a Runnable that waits for specialization
+ * info to start an app process. In the sygote/parent process this returns null.
*/
- static Runnable forkUsap(LocalServerSocket usapPoolSocket,
- int[] sessionSocketRawFDs,
- boolean isPriorityFork) {
- FileDescriptor[] pipeFDs;
+ static @Nullable Runnable forkUsap(LocalServerSocket usapPoolSocket,
+ int[] sessionSocketRawFDs,
+ boolean isPriorityFork) {
+ FileDescriptor readFD;
+ FileDescriptor writeFD;
try {
- pipeFDs = Os.pipe2(O_CLOEXEC);
+ FileDescriptor[] pipeFDs = Os.pipe2(O_CLOEXEC);
+ readFD = pipeFDs[0];
+ writeFD = pipeFDs[1];
} catch (ErrnoException errnoEx) {
throw new IllegalStateException("Unable to create USAP pipe.", errnoEx);
}
- int pid =
- nativeForkUsap(pipeFDs[0].getInt$(), pipeFDs[1].getInt$(),
- sessionSocketRawFDs, isPriorityFork);
-
+ int pid = nativeForkApp(readFD.getInt$(), writeFD.getInt$(),
+ sessionSocketRawFDs, /*argsKnown=*/ false, isPriorityFork);
if (pid == 0) {
- IoUtils.closeQuietly(pipeFDs[0]);
- return usapMain(usapPoolSocket, pipeFDs[1]);
+ IoUtils.closeQuietly(readFD);
+ return childMain(null, usapPoolSocket, writeFD);
+ } else if (pid == -1) {
+ // Fork failed.
+ return null;
} else {
- // The read-end of the pipe will be closed by the native code.
- // See removeUsapTableEntry();
- IoUtils.closeQuietly(pipeFDs[1]);
+ // readFD will be closed by the native code. See removeUsapTableEntry();
+ IoUtils.closeQuietly(writeFD);
+ nativeAddUsapTableEntry(pid, readFD.getInt$());
return null;
}
}
- private static native int nativeForkUsap(int readPipeFD,
- int writePipeFD,
- int[] sessionSocketRawFDs,
- boolean isPriorityFork);
+ private static native int nativeForkApp(int readPipeFD,
+ int writePipeFD,
+ int[] sessionSocketRawFDs,
+ boolean argsKnown,
+ boolean isPriorityFork);
+
+ /**
+ * Add an entry for a new Usap to the table maintained in native code.
+ */
+ @CriticalNative
+ private static native void nativeAddUsapTableEntry(int pid, int readPipeFD);
+
+ /**
+ * Fork a new app process from the zygote. argBuffer contains a fork command that
+ * request neither a child zygote, nor a wrapped process. Continue to accept connections
+ * on the specified socket, use those to refill argBuffer, and continue to process
+ * sufficiently simple fork requests. We presume that the only open file descriptors
+ * requiring special treatment are the session socket embedded in argBuffer, and
+ * zygoteSocket.
+ * @param argBuffer containing initial command and the connected socket from which to
+ * read more
+ * @param zygoteSocket socket from which to obtain new connections when current argBuffer
+ * one is disconnected
+ * @param expectedUId Uid of peer for initial requests. Subsequent requests from a different
+ * peer will cause us to return rather than perform the requested fork.
+ * @param minUid Minimum Uid enforced for all but first fork request. The caller checks
+ * the Uid policy for the initial request.
+ * @param firstNiceName name of first created process. Used for error reporting only.
+ * @return A Runnable in each child process, null in the parent.
+ * If this returns in then argBuffer still contains a command needing to be executed.
+ */
+ static @Nullable Runnable forkSimpleApps(@NonNull ZygoteCommandBuffer argBuffer,
+ @NonNull FileDescriptor zygoteSocket,
+ int expectedUid,
+ int minUid,
+ @Nullable String firstNiceName) {
+ boolean in_child =
+ argBuffer.forkRepeatedly(zygoteSocket, expectedUid, minUid, firstNiceName);
+ if (in_child) {
+ return childMain(argBuffer, /*usapPoolSocket=*/null, /*writePipe=*/null);
+ } else {
+ return null;
+ }
+ }
/**
- * This function is used by unspecialized app processes to wait for specialization requests from
- * the system server.
+ * Specialize the current process into one described by argBuffer or the command read from
+ * usapPoolSocket. Exactly one of those must be null. If we are given an argBuffer, we close
+ * it. Used both for a specializing a USAP process, and for process creation without USAPs.
+ * In both cases, we specialize the process after first returning to Java code.
*
* @param writePipe The write end of the reporting pipe used to communicate with the poll loop
* of the ZygoteServer.
* @return A runnable oject representing the new application.
*/
- private static Runnable usapMain(LocalServerSocket usapPoolSocket,
- FileDescriptor writePipe) {
+ private static Runnable childMain(@Nullable ZygoteCommandBuffer argBuffer,
+ @Nullable LocalServerSocket usapPoolSocket,
+ FileDescriptor writePipe) {
final int pid = Process.myPid();
- Process.setArgV0(Process.is64Bit() ? "usap64" : "usap32");
- LocalSocket sessionSocket = null;
DataOutputStream usapOutputStream = null;
- Credentials peerCredentials = null;
ZygoteArguments args = null;
- // Change the priority to max before calling accept so we can respond to new specialization
- // requests as quickly as possible. This will be reverted to the default priority in the
- // native specialization code.
- boostUsapPriority();
-
- while (true) {
- try {
- sessionSocket = usapPoolSocket.accept();
-
- // Block SIGTERM so we won't be killed if the Zygote flushes the USAP pool.
- blockSigTerm();
-
- BufferedReader usapReader =
- new BufferedReader(new InputStreamReader(sessionSocket.getInputStream()));
- usapOutputStream =
- new DataOutputStream(sessionSocket.getOutputStream());
+ // Block SIGTERM so we won't be killed if the Zygote flushes the USAP pool.
+ blockSigTerm();
- peerCredentials = sessionSocket.getPeerCredentials();
+ LocalSocket sessionSocket = null;
+ if (argBuffer == null) {
+ // Read arguments from usapPoolSocket instead.
- String[] argStrings = readArgumentList(usapReader);
+ Process.setArgV0(Process.is64Bit() ? "usap64" : "usap32");
- if (argStrings != null) {
- args = new ZygoteArguments(argStrings);
+ // Change the priority to max before calling accept so we can respond to new
+ // specialization requests as quickly as possible. This will be reverted to the
+ // default priority in the native specialization code.
+ boostUsapPriority();
+ while (true) {
+ ZygoteCommandBuffer tmpArgBuffer = null;
+ try {
+ sessionSocket = usapPoolSocket.accept();
+
+ usapOutputStream =
+ new DataOutputStream(sessionSocket.getOutputStream());
+ Credentials peerCredentials = sessionSocket.getPeerCredentials();
+ tmpArgBuffer = new ZygoteCommandBuffer(sessionSocket);
+ args = ZygoteArguments.getInstance(argBuffer);
+ applyUidSecurityPolicy(args, peerCredentials);
// TODO (chriswailes): Should this only be run for debug builds?
validateUsapCommand(args);
break;
- } else {
- Log.e("USAP", "Truncated command received.");
- IoUtils.closeQuietly(sessionSocket);
-
- // Re-enable SIGTERM so the USAP can be flushed from the pool if necessary.
- unblockSigTerm();
+ } catch (Exception ex) {
+ Log.e("USAP", ex.getMessage());
}
- } catch (Exception ex) {
- Log.e("USAP", ex.getMessage());
- IoUtils.closeQuietly(sessionSocket);
-
// Re-enable SIGTERM so the USAP can be flushed from the pool if necessary.
unblockSigTerm();
+ IoUtils.closeQuietly(sessionSocket);
+ IoUtils.closeQuietly(tmpArgBuffer);
+ blockSigTerm();
}
+ } else {
+ try {
+ args = ZygoteArguments.getInstance(argBuffer);
+ } catch (Exception ex) {
+ Log.e("AppStartup", ex.getMessage());
+ throw new AssertionError("Failed to parse application start command", ex);
+ }
+ // peerCredentials were checked in parent.
+ }
+ if (args == null) {
+ throw new AssertionError("Empty command line");
}
-
try {
- // SIGTERM is blocked on loop exit. This prevents a USAP that is specializing from
- // being killed during a pool flush.
-
- setAppProcessName(args, "USAP");
+ // SIGTERM is blocked here. This prevents a USAP that is specializing from being
+ // killed during a pool flush.
- applyUidSecurityPolicy(args, peerCredentials);
applyDebuggerSystemProperty(args);
int[][] rlimits = null;
@@ -701,53 +755,57 @@ public final class Zygote {
rlimits = args.mRLimits.toArray(INT_ARRAY_2D);
}
- // This must happen before the SELinux policy for this process is
- // changed when specializing.
- try {
- // Used by ZygoteProcess.zygoteSendArgsAndGetResult to fill in a
- // Process.ProcessStartResult object.
- usapOutputStream.writeInt(pid);
- } catch (IOException ioEx) {
- Log.e("USAP", "Failed to write response to session socket: "
- + ioEx.getMessage());
- throw new RuntimeException(ioEx);
- } finally {
- IoUtils.closeQuietly(sessionSocket);
-
+ if (argBuffer == null) {
+ // This must happen before the SELinux policy for this process is
+ // changed when specializing.
try {
- // This socket is closed using Os.close due to an issue with the implementation
- // of LocalSocketImp.close(). Because the raw FD is created by init and then
- // loaded from an environment variable (as opposed to being created by the
- // LocalSocketImpl itself) the current implementation will not actually close
- // the underlying FD.
- //
- // See b/130309968 for discussion of this issue.
- Os.close(usapPoolSocket.getFileDescriptor());
- } catch (ErrnoException ex) {
- Log.e("USAP", "Failed to close USAP pool socket");
- throw new RuntimeException(ex);
+ // Used by ZygoteProcess.zygoteSendArgsAndGetResult to fill in a
+ // Process.ProcessStartResult object.
+ usapOutputStream.writeInt(pid);
+ } catch (IOException ioEx) {
+ Log.e("USAP", "Failed to write response to session socket: "
+ + ioEx.getMessage());
+ throw new RuntimeException(ioEx);
+ } finally {
+ try {
+ // Since the raw FD is created by init and then loaded from an environment
+ // variable (as opposed to being created by the LocalSocketImpl itself),
+ // the LocalSocket/LocalSocketImpl does not own the Os-level socket. See
+ // the spec for LocalSocket.createConnectedLocalSocket(FileDescriptor fd).
+ // Thus closing the LocalSocket does not suffice. See b/130309968 for more
+ // discussion.
+ FileDescriptor fd = usapPoolSocket.getFileDescriptor();
+ usapPoolSocket.close();
+ Os.close(fd);
+ } catch (ErrnoException | IOException ex) {
+ Log.e("USAP", "Failed to close USAP pool socket");
+ throw new RuntimeException(ex);
+ }
}
}
- try {
- ByteArrayOutputStream buffer =
- new ByteArrayOutputStream(Zygote.USAP_MANAGEMENT_MESSAGE_BYTES);
- DataOutputStream outputStream = new DataOutputStream(buffer);
-
- // This is written as a long so that the USAP reporting pipe and USAP pool event FD
- // handlers in ZygoteServer.runSelectLoop can be unified. These two cases should
- // both send/receive 8 bytes.
- outputStream.writeLong(pid);
- outputStream.flush();
-
- Os.write(writePipe, buffer.toByteArray(), 0, buffer.size());
- } catch (Exception ex) {
- Log.e("USAP",
- String.format("Failed to write PID (%d) to pipe (%d): %s",
- pid, writePipe.getInt$(), ex.getMessage()));
- throw new RuntimeException(ex);
- } finally {
- IoUtils.closeQuietly(writePipe);
+ if (writePipe != null) {
+ try {
+ ByteArrayOutputStream buffer =
+ new ByteArrayOutputStream(Zygote.USAP_MANAGEMENT_MESSAGE_BYTES);
+ DataOutputStream outputStream = new DataOutputStream(buffer);
+
+ // This is written as a long so that the USAP reporting pipe and USAP pool
+ // event FD handlers in ZygoteServer.runSelectLoop can be unified. These two
+ // cases should both send/receive 8 bytes.
+ // TODO: Needs tweaking to handle the non-Usap invoke-with case, which expects
+ // a different format.
+ outputStream.writeLong(pid);
+ outputStream.flush();
+ Os.write(writePipe, buffer.toByteArray(), 0, buffer.size());
+ } catch (Exception ex) {
+ Log.e("USAP",
+ String.format("Failed to write PID (%d) to pipe (%d): %s",
+ pid, writePipe.getInt$(), ex.getMessage()));
+ throw new RuntimeException(ex);
+ } finally {
+ IoUtils.closeQuietly(writePipe);
+ }
}
specializeAppProcess(args.mUid, args.mGid, args.mGids,
@@ -854,13 +912,29 @@ public final class Zygote {
return nativeRemoveUsapTableEntry(usapPID);
}
+ @CriticalNative
private static native boolean nativeRemoveUsapTableEntry(int usapPID);
/**
- * uid 1000 (Process.SYSTEM_UID) may specify any uid &gt; 1000 in normal
+ * Return the minimum child uid that the given peer is allowed to create.
+ * uid 1000 (Process.SYSTEM_UID) may specify any uid &ge; 1000 in normal
* operation. It may also specify any gid and setgroups() list it chooses.
* In factory test mode, it may specify any UID.
- *
+ */
+ static int minChildUid(Credentials peer) {
+ if (peer.getUid() == Process.SYSTEM_UID
+ && FactoryTest.getMode() == FactoryTest.FACTORY_TEST_OFF) {
+ /* In normal operation, SYSTEM_UID can only specify a restricted
+ * set of UIDs. In factory test mode, SYSTEM_UID may specify any uid.
+ */
+ return Process.SYSTEM_UID;
+ } else {
+ return 0;
+ }
+ }
+
+ /*
+ * Adjust uid and gid arguments, ensuring that the security policy is satisfied.
* @param args non-null; zygote spawner arguments
* @param peer non-null; peer credentials
* @throws ZygoteSecurityException Indicates a security issue when applying the UID based
@@ -869,17 +943,10 @@ public final class Zygote {
static void applyUidSecurityPolicy(ZygoteArguments args, Credentials peer)
throws ZygoteSecurityException {
- if (peer.getUid() == Process.SYSTEM_UID) {
- /* In normal operation, SYSTEM_UID can only specify a restricted
- * set of UIDs. In factory test mode, SYSTEM_UID may specify any uid.
- */
- boolean uidRestricted = FactoryTest.getMode() == FactoryTest.FACTORY_TEST_OFF;
-
- if (uidRestricted && args.mUidSpecified && (args.mUid < Process.SYSTEM_UID)) {
- throw new ZygoteSecurityException(
- "System UID may not launch process with UID < "
- + Process.SYSTEM_UID);
- }
+ if (args.mUidSpecified && (args.mUid < minChildUid(peer))) {
+ throw new ZygoteSecurityException(
+ "System UID may not launch process with UID < "
+ + Process.SYSTEM_UID);
}
// If not otherwise specified, uid and gid are inherited from peer
@@ -965,45 +1032,6 @@ public final class Zygote {
}
/**
- * Reads an argument list from the provided socket
- * @return Argument list or null if EOF is reached
- * @throws IOException passed straight through
- */
- static String[] readArgumentList(BufferedReader socketReader) throws IOException {
- int argc;
-
- try {
- String argc_string = socketReader.readLine();
-
- if (argc_string == null) {
- // EOF reached.
- return null;
- }
- argc = Integer.parseInt(argc_string);
-
- } catch (NumberFormatException ex) {
- Log.e("Zygote", "Invalid Zygote wire format: non-int at argc");
- throw new IOException("Invalid wire format");
- }
-
- // See bug 1092107: large argc can be used for a DOS attack
- if (argc > MAX_ZYGOTE_ARGC) {
- throw new IOException("Max arg count exceeded");
- }
-
- String[] args = new String[argc];
- for (int arg_index = 0; arg_index < argc; arg_index++) {
- args[arg_index] = socketReader.readLine();
- if (args[arg_index] == null) {
- // We got an unexpected EOF.
- throw new IOException("Truncated request");
- }
- }
-
- return args;
- }
-
- /**
* Creates a managed LocalServerSocket object using a file descriptor
* created by an init.rc script. The init scripts that specify the
* sockets name can be found in system/core/rootdir. The socket is bound
diff --git a/core/java/com/android/internal/os/ZygoteArguments.java b/core/java/com/android/internal/os/ZygoteArguments.java
index ed074327c3c5..5a1c1710d32b 100644
--- a/core/java/com/android/internal/os/ZygoteArguments.java
+++ b/core/java/com/android/internal/os/ZygoteArguments.java
@@ -16,8 +16,8 @@
package com.android.internal.os;
+import java.io.EOFException;
import java.util.ArrayList;
-import java.util.Arrays;
/**
* Handles argument parsing for args related to the zygote spawner.
@@ -245,20 +245,34 @@ class ZygoteArguments {
/**
* Constructs instance and parses args
*
- * @param args zygote command-line args
+ * @param args zygote command-line args as ZygoteCommandBuffer, positioned after argument count.
*/
- ZygoteArguments(String[] args) throws IllegalArgumentException {
- parseArgs(args);
+ private ZygoteArguments(ZygoteCommandBuffer args, int argCount)
+ throws IllegalArgumentException, EOFException {
+ parseArgs(args, argCount);
+ }
+
+ /**
+ * Return a new ZygoteArguments reflecting the contents of the given ZygoteCommandBuffer. Return
+ * null if the ZygoteCommandBuffer was positioned at EOF. Assumes the buffer is initially
+ * positioned at the beginning of the command.
+ */
+ public static ZygoteArguments getInstance(ZygoteCommandBuffer args)
+ throws IllegalArgumentException, EOFException {
+ int argCount = args.getCount();
+ return argCount == 0 ? null : new ZygoteArguments(args, argCount);
}
/**
* Parses the commandline arguments intended for the Zygote spawner (such as "--setuid=" and
- * "--setgid=") and creates an array containing the remaining args.
+ * "--setgid=") and creates an array containing the remaining args. Return false if we were
+ * at EOF.
*
* Per security review bug #1112214, duplicate args are disallowed in critical cases to make
* injection harder.
*/
- private void parseArgs(String[] args) throws IllegalArgumentException {
+ private void parseArgs(ZygoteCommandBuffer args, int argCount)
+ throws IllegalArgumentException, EOFException {
/*
* See android.os.ZygoteProcess.zygoteSendArgsAndGetResult()
* Presently the wire format to the zygote process is:
@@ -269,13 +283,13 @@ class ZygoteArguments {
* the child or -1 on failure.
*/
- int curArg = 0;
-
+ String unprocessedArg = null;
+ int curArg = 0; // Index of arg
boolean seenRuntimeArgs = false;
-
boolean expectRuntimeArgs = true;
- for ( /* curArg */ ; curArg < args.length; curArg++) {
- String arg = args[curArg];
+
+ for ( /* curArg */ ; curArg < argCount; ++curArg) {
+ String arg = args.nextArg();
if (arg.equals("--")) {
curArg++;
@@ -367,7 +381,8 @@ class ZygoteArguments {
"Duplicate arg specified");
}
try {
- mInvokeWith = args[++curArg];
+ ++curArg;
+ mInvokeWith = args.nextArg();
} catch (IndexOutOfBoundsException ex) {
throw new IllegalArgumentException(
"--invoke-with requires argument");
@@ -405,12 +420,14 @@ class ZygoteArguments {
} else if (arg.startsWith("--app-data-dir=")) {
mAppDataDir = getAssignmentValue(arg);
} else if (arg.equals("--preload-app")) {
- mPreloadApp = args[++curArg];
+ ++curArg;
+ mPreloadApp = args.nextArg();
} else if (arg.equals("--preload-package")) {
- mPreloadPackage = args[++curArg];
- mPreloadPackageLibs = args[++curArg];
- mPreloadPackageLibFileName = args[++curArg];
- mPreloadPackageCacheKey = args[++curArg];
+ curArg += 4;
+ mPreloadPackage = args.nextArg();
+ mPreloadPackageLibs = args.nextArg();
+ mPreloadPackageLibFileName = args.nextArg();
+ mPreloadPackageCacheKey = args.nextArg();
} else if (arg.equals("--preload-default")) {
mPreloadDefault = true;
expectRuntimeArgs = false;
@@ -419,8 +436,11 @@ class ZygoteArguments {
} else if (arg.equals("--set-api-denylist-exemptions")) {
// consume all remaining args; this is a stand-alone command, never included
// with the regular fork command.
- mApiDenylistExemptions = Arrays.copyOfRange(args, curArg + 1, args.length);
- curArg = args.length;
+ mApiDenylistExemptions = new String[argCount - curArg - 1];
+ ++curArg;
+ for (int i = 0; curArg < argCount; ++curArg, ++i) {
+ mApiDenylistExemptions[i] = args.nextArg();
+ }
expectRuntimeArgs = false;
} else if (arg.startsWith("--hidden-api-log-sampling-rate=")) {
String rateStr = getAssignmentValue(arg);
@@ -470,35 +490,46 @@ class ZygoteArguments {
} else if (arg.equals(Zygote.BIND_MOUNT_APP_DATA_DIRS)) {
mBindMountAppDataDirs = true;
} else {
+ unprocessedArg = arg;
break;
}
}
+ // curArg is the index of the first unprocessed argument. That argument is either referenced
+ // by unprocessedArg or not read yet.
if (mBootCompleted) {
- if (args.length - curArg > 0) {
+ if (argCount > curArg) {
throw new IllegalArgumentException("Unexpected arguments after --boot-completed");
}
} else if (mAbiListQuery || mPidQuery) {
- if (args.length - curArg > 0) {
+ if (argCount > curArg) {
throw new IllegalArgumentException("Unexpected arguments after --query-abi-list.");
}
} else if (mPreloadPackage != null) {
- if (args.length - curArg > 0) {
+ if (argCount > curArg) {
throw new IllegalArgumentException(
"Unexpected arguments after --preload-package.");
}
} else if (mPreloadApp != null) {
- if (args.length - curArg > 0) {
+ if (argCount > curArg) {
throw new IllegalArgumentException(
"Unexpected arguments after --preload-app.");
}
} else if (expectRuntimeArgs) {
if (!seenRuntimeArgs) {
- throw new IllegalArgumentException("Unexpected argument : " + args[curArg]);
+ throw new IllegalArgumentException("Unexpected argument : "
+ + (unprocessedArg == null ? args.nextArg() : unprocessedArg));
}
- mRemainingArgs = new String[args.length - curArg];
- System.arraycopy(args, curArg, mRemainingArgs, 0, mRemainingArgs.length);
+ mRemainingArgs = new String[argCount - curArg];
+ int i = 0;
+ if (unprocessedArg != null) {
+ mRemainingArgs[0] = unprocessedArg;
+ ++i;
+ }
+ for (; i < argCount - curArg; ++i) {
+ mRemainingArgs[i] = args.nextArg();
+ }
}
if (mStartChildZygote) {
diff --git a/core/java/com/android/internal/os/ZygoteCommandBuffer.java b/core/java/com/android/internal/os/ZygoteCommandBuffer.java
new file mode 100644
index 000000000000..b61ae7acfacd
--- /dev/null
+++ b/core/java/com/android/internal/os/ZygoteCommandBuffer.java
@@ -0,0 +1,187 @@
+/*
+ * 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.annotation.NonNull;
+import android.annotation.Nullable;
+import android.net.LocalSocket;
+
+import java.io.FileDescriptor;
+import java.lang.ref.Reference; // For reachabilityFence.
+
+/**
+ * A native-accessible buffer for Zygote commands. Designed to support repeated forking
+ * of applications without intervening memory allocation, thus keeping zygote memory
+ * as stable as possible.
+ * A ZygoteCommandBuffer may have an associated socket from which it can be refilled.
+ * Otherwise the contents are explicitly set by getInstance().
+ *
+ * NOT THREAD-SAFE. No methods may be called concurrently from multiple threads.
+ *
+ * Only one ZygoteCommandBuffer can exist at a time.
+ * Must be explicitly closed before being dropped.
+ * @hide
+ */
+class ZygoteCommandBuffer implements AutoCloseable {
+ private long mNativeBuffer; // Not final so that we can clear it in close().
+
+ /**
+ * The command socket.
+ *
+ * mSocket is retained in the child process in "peer wait" mode, so
+ * that it closes when the child process terminates. In other cases,
+ * it is closed in the peer.
+ */
+ private final LocalSocket mSocket;
+ private final int mNativeSocket;
+
+ /**
+ * Constructs instance from file descriptor from which the command will be read.
+ * Only a single instance may be live in a given process. The native code checks.
+ *
+ * @param fd file descriptor to read from. The setCommand() method may be used if and only if
+ * fd is null.
+ */
+ ZygoteCommandBuffer(@Nullable LocalSocket socket) {
+ mSocket = socket;
+ if (socket == null) {
+ mNativeSocket = -1;
+ } else {
+ mNativeSocket = mSocket.getFileDescriptor().getInt$();
+ }
+ mNativeBuffer = getNativeBuffer(mNativeSocket);
+ }
+
+ /**
+ * Constructs an instance with explicitly supplied arguments and an invalid
+ * file descriptor. Can only be used for a single command.
+ */
+ ZygoteCommandBuffer(@NonNull String[] args) {
+ this((LocalSocket) null);
+ setCommand(args);
+ }
+
+
+ private static native long getNativeBuffer(int fd);
+
+ /**
+ * Deallocate native resources associated with the one and only command buffer, and prevent
+ * reuse. Subsequent calls to getInstance() will yield a new buffer.
+ * We do not close the associated socket, if any.
+ */
+ @Override
+ public void close() {
+ freeNativeBuffer(mNativeBuffer);
+ mNativeBuffer = 0;
+ }
+
+ private static native void freeNativeBuffer(long /* NativeCommandBuffer* */ nbuffer);
+
+ /**
+ * Read at least the first line of the next command into the buffer, return the argument count
+ * from that line. Assumes we are initially positioned at the beginning of the first line of
+ * the command. Leave the buffer positioned at the beginning of the second command line, i.e.
+ * the first argument. If the buffer has no associated file descriptor, we just reposition to
+ * the beginning of the buffer, and reread existing contents. Returns zero if we started out
+ * at EOF.
+ */
+ int getCount() {
+ try {
+ return nativeGetCount(mNativeBuffer);
+ } finally {
+ // Make sure the mNativeSocket doesn't get closed due to early finalization.
+ Reference.reachabilityFence(mSocket);
+ }
+ }
+
+ private static native int nativeGetCount(long /* NativeCommandBuffer* */ nbuffer);
+
+
+ /*
+ * Set the buffer to contain the supplied sequence of arguments.
+ */
+ private void setCommand(String[] command) {
+ int nArgs = command.length;
+ insert(mNativeBuffer, Integer.toString(nArgs));
+ for (String s: command) {
+ insert(mNativeBuffer, s);
+ }
+ // Native code checks there is no socket; hence no reachabilityFence.
+ }
+
+ private static native void insert(long /* NativeCommandBuffer* */ nbuffer, String s);
+
+ /**
+ * Retrieve the next argument/line from the buffer, filling the buffer as necessary.
+ */
+ String nextArg() {
+ try {
+ return nativeNextArg(mNativeBuffer);
+ } finally {
+ Reference.reachabilityFence(mSocket);
+ }
+ }
+
+ private static native String nativeNextArg(long /* NativeCommandBuffer* */ nbuffer);
+
+ void readFullyAndReset() {
+ try {
+ nativeReadFullyAndReset(mNativeBuffer);
+ } finally {
+ Reference.reachabilityFence(mSocket);
+ }
+ }
+
+ private static native void nativeReadFullyAndReset(long /* NativeCommandBuffer* */ nbuffer);
+
+ /**
+ * Fork a child as specified by the current command in the buffer, and repeat this process
+ * after refilling the buffer, so long as the buffer clearly contains another fork command.
+ *
+ * @param zygoteSocket socket from which to obtain new connections when current one is
+ * disconnected
+ * @param expectedUid Peer UID for current connection. We refuse to deal with requests from
+ * a different UID.
+ * @param minUid the smallest uid that may be request for the child process.
+ * @param firstNiceName The name for the initial process to be forked. Used only for error
+ * reporting.
+ *
+ * @return true in the child, false in the parent. In the parent case, the buffer is positioned
+ * at the beginning of a command that still needs to be processed.
+ */
+ boolean forkRepeatedly(FileDescriptor zygoteSocket, int expectedUid, int minUid,
+ String firstNiceName) {
+ try {
+ return nativeForkRepeatedly(mNativeBuffer, zygoteSocket.getInt$(),
+ expectedUid, minUid, firstNiceName);
+ } finally {
+ Reference.reachabilityFence(mSocket);
+ Reference.reachabilityFence(zygoteSocket);
+ }
+ }
+
+ /*
+ * Repeatedly fork children as above. It commonly does not return in the parent, but it may.
+ * @return true in the chaild, false in the parent if we encounter a command we couldn't handle.
+ */
+ private static native boolean nativeForkRepeatedly(long /* NativeCommandBuffer* */ nbuffer,
+ int zygoteSocketRawFd,
+ int expectedUid,
+ int minUid,
+ String firstNiceName);
+
+}
diff --git a/core/java/com/android/internal/os/ZygoteConnection.java b/core/java/com/android/internal/os/ZygoteConnection.java
index 5a576ebbc442..37c75907061c 100644
--- a/core/java/com/android/internal/os/ZygoteConnection.java
+++ b/core/java/com/android/internal/os/ZygoteConnection.java
@@ -36,16 +36,15 @@ import android.system.StructPollfd;
import android.util.Log;
import dalvik.system.VMRuntime;
+import dalvik.system.ZygoteHooks;
import libcore.io.IoUtils;
-import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.FileDescriptor;
import java.io.IOException;
-import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
import java.util.concurrent.TimeUnit;
@@ -67,7 +66,6 @@ class ZygoteConnection {
private final LocalSocket mSocket;
@UnsupportedAppUsage
private final DataOutputStream mSocketOutStream;
- private final BufferedReader mSocketReader;
@UnsupportedAppUsage
private final Credentials peer;
private final String abiList;
@@ -85,9 +83,6 @@ class ZygoteConnection {
this.abiList = abiList;
mSocketOutStream = new DataOutputStream(socket.getOutputStream());
- mSocketReader =
- new BufferedReader(
- new InputStreamReader(socket.getInputStream()), Zygote.SOCKET_BUFFER_SIZE);
mSocket.setSoTimeout(CONNECTION_TIMEOUT_MILLIS);
@@ -111,178 +106,216 @@ class ZygoteConnection {
}
/**
- * Reads one start command from the command socket. If successful, a child is forked and a
+ * Reads a command from the command socket. If a child is successfully forked, a
* {@code Runnable} that calls the childs main method (or equivalent) is returned in the child
* process. {@code null} is always returned in the parent process (the zygote).
+ * If multipleOK is set, we may keep processing additional fork commands before returning.
*
* If the client closes the socket, an {@code EOF} condition is set, which callers can test
* for by calling {@code ZygoteConnection.isClosedByPeer}.
*/
- Runnable processOneCommand(ZygoteServer zygoteServer) {
- String[] args;
-
- try {
- args = Zygote.readArgumentList(mSocketReader);
- } catch (IOException ex) {
- throw new IllegalStateException("IOException on command socket", ex);
- }
-
- // readArgumentList returns null only when it has reached EOF with no available
- // data to read. This will only happen when the remote socket has disconnected.
- if (args == null) {
- isEof = true;
- return null;
- }
-
- int pid;
- FileDescriptor childPipeFd = null;
- FileDescriptor serverPipeFd = null;
-
- ZygoteArguments parsedArgs = new ZygoteArguments(args);
-
- if (parsedArgs.mBootCompleted) {
- handleBootCompleted();
- return null;
- }
-
- if (parsedArgs.mAbiListQuery) {
- handleAbiListQuery();
- return null;
- }
-
- if (parsedArgs.mPidQuery) {
- handlePidQuery();
- return null;
- }
+ Runnable processCommand(ZygoteServer zygoteServer, boolean multipleOK) {
+ ZygoteArguments parsedArgs;
+
+ try (ZygoteCommandBuffer argBuffer = new ZygoteCommandBuffer(mSocket)) {
+ while (true) {
+ try {
+ parsedArgs = ZygoteArguments.getInstance(argBuffer);
+ // Keep argBuffer around, since we need it to fork.
+ } catch (IOException ex) {
+ throw new IllegalStateException("IOException on command socket", ex);
+ }
+ if (parsedArgs == null) {
+ isEof = true;
+ return null;
+ }
- if (parsedArgs.mUsapPoolStatusSpecified) {
- return handleUsapPoolStatusChange(zygoteServer, parsedArgs.mUsapPoolEnabled);
- }
+ int pid;
+ FileDescriptor childPipeFd = null;
+ FileDescriptor serverPipeFd = null;
- if (parsedArgs.mPreloadDefault) {
- handlePreload();
- return null;
- }
+ if (parsedArgs.mBootCompleted) {
+ handleBootCompleted();
+ return null;
+ }
- if (parsedArgs.mPreloadPackage != null) {
- handlePreloadPackage(parsedArgs.mPreloadPackage, parsedArgs.mPreloadPackageLibs,
- parsedArgs.mPreloadPackageLibFileName, parsedArgs.mPreloadPackageCacheKey);
- return null;
- }
+ if (parsedArgs.mAbiListQuery) {
+ handleAbiListQuery();
+ return null;
+ }
- if (canPreloadApp() && parsedArgs.mPreloadApp != null) {
- byte[] rawParcelData = Base64.getDecoder().decode(parsedArgs.mPreloadApp);
- Parcel appInfoParcel = Parcel.obtain();
- appInfoParcel.unmarshall(rawParcelData, 0, rawParcelData.length);
- appInfoParcel.setDataPosition(0);
- ApplicationInfo appInfo = ApplicationInfo.CREATOR.createFromParcel(appInfoParcel);
- appInfoParcel.recycle();
- if (appInfo != null) {
- handlePreloadApp(appInfo);
- } else {
- throw new IllegalArgumentException("Failed to deserialize --preload-app");
- }
- return null;
- }
+ if (parsedArgs.mPidQuery) {
+ handlePidQuery();
+ return null;
+ }
- if (parsedArgs.mApiDenylistExemptions != null) {
- return handleApiDenylistExemptions(zygoteServer, parsedArgs.mApiDenylistExemptions);
- }
+ if (parsedArgs.mUsapPoolStatusSpecified) {
+ // Handle this once we've released the argBuffer, to avoid opening a second one.
+ break;
+ }
- if (parsedArgs.mHiddenApiAccessLogSampleRate != -1
- || parsedArgs.mHiddenApiAccessStatslogSampleRate != -1) {
- return handleHiddenApiAccessLogSampleRate(zygoteServer,
- parsedArgs.mHiddenApiAccessLogSampleRate,
- parsedArgs.mHiddenApiAccessStatslogSampleRate);
- }
+ if (parsedArgs.mPreloadDefault) {
+ handlePreload();
+ return null;
+ }
- if (parsedArgs.mPermittedCapabilities != 0 || parsedArgs.mEffectiveCapabilities != 0) {
- throw new ZygoteSecurityException("Client may not specify capabilities: "
- + "permitted=0x" + Long.toHexString(parsedArgs.mPermittedCapabilities)
- + ", effective=0x" + Long.toHexString(parsedArgs.mEffectiveCapabilities));
- }
+ if (parsedArgs.mPreloadPackage != null) {
+ handlePreloadPackage(parsedArgs.mPreloadPackage,
+ parsedArgs.mPreloadPackageLibs,
+ parsedArgs.mPreloadPackageLibFileName,
+ parsedArgs.mPreloadPackageCacheKey);
+ return null;
+ }
- Zygote.applyUidSecurityPolicy(parsedArgs, peer);
- Zygote.applyInvokeWithSecurityPolicy(parsedArgs, peer);
+ if (canPreloadApp() && parsedArgs.mPreloadApp != null) {
+ byte[] rawParcelData = Base64.getDecoder().decode(parsedArgs.mPreloadApp);
+ Parcel appInfoParcel = Parcel.obtain();
+ appInfoParcel.unmarshall(rawParcelData, 0, rawParcelData.length);
+ appInfoParcel.setDataPosition(0);
+ ApplicationInfo appInfo =
+ ApplicationInfo.CREATOR.createFromParcel(appInfoParcel);
+ appInfoParcel.recycle();
+ if (appInfo != null) {
+ handlePreloadApp(appInfo);
+ } else {
+ throw new IllegalArgumentException("Failed to deserialize --preload-app");
+ }
+ return null;
+ }
- Zygote.applyDebuggerSystemProperty(parsedArgs);
- Zygote.applyInvokeWithSystemProperty(parsedArgs);
+ if (parsedArgs.mApiDenylistExemptions != null) {
+ return handleApiDenylistExemptions(zygoteServer,
+ parsedArgs.mApiDenylistExemptions);
+ }
- int[][] rlimits = null;
+ if (parsedArgs.mHiddenApiAccessLogSampleRate != -1
+ || parsedArgs.mHiddenApiAccessStatslogSampleRate != -1) {
+ return handleHiddenApiAccessLogSampleRate(zygoteServer,
+ parsedArgs.mHiddenApiAccessLogSampleRate,
+ parsedArgs.mHiddenApiAccessStatslogSampleRate);
+ }
- if (parsedArgs.mRLimits != null) {
- rlimits = parsedArgs.mRLimits.toArray(Zygote.INT_ARRAY_2D);
- }
+ if (parsedArgs.mPermittedCapabilities != 0
+ || parsedArgs.mEffectiveCapabilities != 0) {
+ throw new ZygoteSecurityException("Client may not specify capabilities: "
+ + "permitted=0x" + Long.toHexString(parsedArgs.mPermittedCapabilities)
+ + ", effective=0x"
+ + Long.toHexString(parsedArgs.mEffectiveCapabilities));
+ }
- int[] fdsToIgnore = null;
+ Zygote.applyUidSecurityPolicy(parsedArgs, peer);
+ Zygote.applyInvokeWithSecurityPolicy(parsedArgs, peer);
- if (parsedArgs.mInvokeWith != null) {
- try {
- FileDescriptor[] pipeFds = Os.pipe2(O_CLOEXEC);
- childPipeFd = pipeFds[1];
- serverPipeFd = pipeFds[0];
- Os.fcntlInt(childPipeFd, F_SETFD, 0);
- fdsToIgnore = new int[]{childPipeFd.getInt$(), serverPipeFd.getInt$()};
- } catch (ErrnoException errnoEx) {
- throw new IllegalStateException("Unable to set up pipe for invoke-with", errnoEx);
- }
- }
+ Zygote.applyDebuggerSystemProperty(parsedArgs);
+ Zygote.applyInvokeWithSystemProperty(parsedArgs);
- /*
- * In order to avoid leaking descriptors to the Zygote child,
- * the native code must close the two Zygote socket descriptors
- * in the child process before it switches from Zygote-root to
- * the UID and privileges of the application being launched.
- *
- * In order to avoid "bad file descriptor" errors when the
- * two LocalSocket objects are closed, the Posix file
- * descriptors are released via a dup2() call which closes
- * the socket and substitutes an open descriptor to /dev/null.
- */
+ int[][] rlimits = null;
- int [] fdsToClose = { -1, -1 };
+ if (parsedArgs.mRLimits != null) {
+ rlimits = parsedArgs.mRLimits.toArray(Zygote.INT_ARRAY_2D);
+ }
- FileDescriptor fd = mSocket.getFileDescriptor();
+ int[] fdsToIgnore = null;
+
+ if (parsedArgs.mInvokeWith != null) {
+ try {
+ FileDescriptor[] pipeFds = Os.pipe2(O_CLOEXEC);
+ childPipeFd = pipeFds[1];
+ serverPipeFd = pipeFds[0];
+ Os.fcntlInt(childPipeFd, F_SETFD, 0);
+ fdsToIgnore = new int[]{childPipeFd.getInt$(), serverPipeFd.getInt$()};
+ } catch (ErrnoException errnoEx) {
+ throw new IllegalStateException("Unable to set up pipe for invoke-with",
+ errnoEx);
+ }
+ }
- if (fd != null) {
- fdsToClose[0] = fd.getInt$();
- }
+ /*
+ * In order to avoid leaking descriptors to the Zygote child,
+ * the native code must close the two Zygote socket descriptors
+ * in the child process before it switches from Zygote-root to
+ * the UID and privileges of the application being launched.
+ *
+ * In order to avoid "bad file descriptor" errors when the
+ * two LocalSocket objects are closed, the Posix file
+ * descriptors are released via a dup2() call which closes
+ * the socket and substitutes an open descriptor to /dev/null.
+ */
- fd = zygoteServer.getZygoteSocketFileDescriptor();
+ int [] fdsToClose = { -1, -1 };
- if (fd != null) {
- fdsToClose[1] = fd.getInt$();
- }
+ FileDescriptor fd = mSocket.getFileDescriptor();
- pid = Zygote.forkAndSpecialize(parsedArgs.mUid, parsedArgs.mGid, parsedArgs.mGids,
- parsedArgs.mRuntimeFlags, rlimits, parsedArgs.mMountExternal, parsedArgs.mSeInfo,
- parsedArgs.mNiceName, fdsToClose, fdsToIgnore, parsedArgs.mStartChildZygote,
- parsedArgs.mInstructionSet, parsedArgs.mAppDataDir, parsedArgs.mIsTopApp,
- parsedArgs.mPkgDataInfoList, parsedArgs.mWhitelistedDataInfoList,
- parsedArgs.mBindMountAppDataDirs, parsedArgs.mBindMountAppStorageDirs);
+ if (fd != null) {
+ fdsToClose[0] = fd.getInt$();
+ }
- try {
- if (pid == 0) {
- // in child
- zygoteServer.setForkChild();
+ FileDescriptor zygoteFd = zygoteServer.getZygoteSocketFileDescriptor();
- zygoteServer.closeServerSocket();
- IoUtils.closeQuietly(serverPipeFd);
- serverPipeFd = null;
+ if (zygoteFd != null) {
+ fdsToClose[1] = zygoteFd.getInt$();
+ }
- return handleChildProc(parsedArgs, childPipeFd, parsedArgs.mStartChildZygote);
- } else {
- // In the parent. A pid < 0 indicates a failure and will be handled in
- // handleParentProc.
- IoUtils.closeQuietly(childPipeFd);
- childPipeFd = null;
- handleParentProc(pid, serverPipeFd);
- return null;
+ if (parsedArgs.mInvokeWith != null || parsedArgs.mStartChildZygote
+ || !multipleOK || peer.getUid() != Process.SYSTEM_UID) {
+ // Continue using old code for now. TODO: Handle these cases in the other path.
+ pid = Zygote.forkAndSpecialize(parsedArgs.mUid, parsedArgs.mGid,
+ parsedArgs.mGids, parsedArgs.mRuntimeFlags, rlimits,
+ parsedArgs.mMountExternal, parsedArgs.mSeInfo, parsedArgs.mNiceName,
+ fdsToClose, fdsToIgnore, parsedArgs.mStartChildZygote,
+ parsedArgs.mInstructionSet, parsedArgs.mAppDataDir,
+ parsedArgs.mIsTopApp, parsedArgs.mPkgDataInfoList,
+ parsedArgs.mWhitelistedDataInfoList, parsedArgs.mBindMountAppDataDirs,
+ parsedArgs.mBindMountAppStorageDirs);
+
+ try {
+ if (pid == 0) {
+ // in child
+ zygoteServer.setForkChild();
+
+ zygoteServer.closeServerSocket();
+ IoUtils.closeQuietly(serverPipeFd);
+ serverPipeFd = null;
+
+ return handleChildProc(parsedArgs, childPipeFd,
+ parsedArgs.mStartChildZygote);
+ } else {
+ // In the parent. A pid < 0 indicates a failure and will be handled in
+ // handleParentProc.
+ IoUtils.closeQuietly(childPipeFd);
+ childPipeFd = null;
+ handleParentProc(pid, serverPipeFd);
+ return null;
+ }
+ } finally {
+ IoUtils.closeQuietly(childPipeFd);
+ IoUtils.closeQuietly(serverPipeFd);
+ }
+ } else {
+ ZygoteHooks.preFork();
+ Runnable result = Zygote.forkSimpleApps(argBuffer,
+ zygoteServer.getZygoteSocketFileDescriptor(),
+ peer.getUid(), Zygote.minChildUid(peer), parsedArgs.mNiceName);
+ if (result == null) {
+ // parent; we finished some number of forks. Result is Boolean.
+ // We already did the equivalent of handleParentProc().
+ ZygoteHooks.postForkCommon();
+ // argBuffer contains a command not understood by forksimpleApps.
+ continue;
+ } else {
+ // child; result is a Runnable.
+ zygoteServer.setForkChild();
+ Zygote.setAppProcessName(parsedArgs, TAG); // ??? Necessary?
+ return result;
+ }
+ }
}
- } finally {
- IoUtils.closeQuietly(childPipeFd);
- IoUtils.closeQuietly(serverPipeFd);
}
+ if (parsedArgs.mUsapPoolStatusSpecified) {
+ // Now that we've released argBuffer:
+ return handleUsapPoolStatusChange(zygoteServer, parsedArgs.mUsapPoolEnabled);
+ }
+ throw new AssertionError("Shouldn't get here");
}
private void handleAbiListQuery() {
@@ -557,7 +590,7 @@ class ZygoteConnection {
if (res > 0) {
if ((fds[0].revents & POLLIN) != 0) {
- // Only read one byte, so as not to block.
+ // Only read one byte, so as not to block. Really needed?
int readBytes = android.system.Os.read(pipeFd, data, dataIndex, 1);
if (readBytes < 0) {
throw new RuntimeException("Some error");
diff --git a/core/java/com/android/internal/os/ZygoteConnectionConstants.java b/core/java/com/android/internal/os/ZygoteConnectionConstants.java
index 506e39f30617..0c1cd6de1bb4 100644
--- a/core/java/com/android/internal/os/ZygoteConnectionConstants.java
+++ b/core/java/com/android/internal/os/ZygoteConnectionConstants.java
@@ -31,9 +31,6 @@ public class ZygoteConnectionConstants {
*/
public static final int CONNECTION_TIMEOUT_MILLIS = 1000;
- /** max number of arguments that a connection can specify */
- public static final int MAX_ZYGOTE_ARGC = 1024;
-
/**
* Wait time for a wrapped app to report back its pid.
*
diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java
index ef1c50f43681..fe87b64940fb 100644
--- a/core/java/com/android/internal/os/ZygoteInit.java
+++ b/core/java/com/android/internal/os/ZygoteInit.java
@@ -65,6 +65,7 @@ import dalvik.system.ZygoteHooks;
import libcore.io.IoUtils;
import java.io.BufferedReader;
+import java.io.EOFException;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
@@ -779,7 +780,13 @@ public class ZygoteInit {
int pid;
try {
- parsedArgs = new ZygoteArguments(args);
+ ZygoteCommandBuffer commandBuffer = new ZygoteCommandBuffer(args);
+ try {
+ parsedArgs = ZygoteArguments.getInstance(commandBuffer);
+ } catch (EOFException e) {
+ throw new AssertionError("Unexpected argument error for forking system server", e);
+ }
+ commandBuffer.close();
Zygote.applyDebuggerSystemProperty(parsedArgs);
Zygote.applyInvokeWithSystemProperty(parsedArgs);
@@ -858,7 +865,7 @@ public class ZygoteInit {
* into new processes are required to either set the priority to the default value or terminate
* before executing any non-system code. The native side of this occurs in SpecializeCommon,
* while the Java Language priority is changed in ZygoteInit.handleSystemServerProcess,
- * ZygoteConnection.handleChildProc, and Zygote.usapMain.
+ * ZygoteConnection.handleChildProc, and Zygote.childMain.
*
* @param argv Command line arguments used to specify the Zygote's configuration.
*/
diff --git a/core/java/com/android/internal/os/ZygoteServer.java b/core/java/com/android/internal/os/ZygoteServer.java
index 585ddf6ddf98..f71b31493035 100644
--- a/core/java/com/android/internal/os/ZygoteServer.java
+++ b/core/java/com/android/internal/os/ZygoteServer.java
@@ -337,7 +337,7 @@ class ZygoteServer {
* @param sessionSocketRawFDs Anonymous session sockets that are currently open
* @return In the Zygote process this function will always return null; in unspecialized app
* processes this function will return a Runnable object representing the new
- * application that is passed up from usapMain.
+ * application that is passed up from childMain (the usap's main wait loop).
*/
Runnable fillUsapPool(int[] sessionSocketRawFDs, boolean isPriorityRefill) {
@@ -420,6 +420,7 @@ class ZygoteServer {
* Runs the zygote process's select loop. Accepts new connections as
* they happen, and reads commands from connections one spawn-request's
* worth at a time.
+ * @param abiList list of ABIs supported by this zygote.
*/
Runnable runSelectLoop(String abiList) {
ArrayList<FileDescriptor> socketFDs = new ArrayList<>();
@@ -537,22 +538,23 @@ class ZygoteServer {
if (pollIndex == 0) {
// Zygote server socket
-
ZygoteConnection newPeer = acceptCommandPeer(abiList);
peers.add(newPeer);
socketFDs.add(newPeer.getFileDescriptor());
-
} else if (pollIndex < usapPoolEventFDIndex) {
// Session socket accepted from the Zygote server socket
try {
ZygoteConnection connection = peers.get(pollIndex);
- final Runnable command = connection.processOneCommand(this);
+ boolean multipleForksOK = !isUsapPoolEnabled()
+ && ZygoteHooks.indefiniteThreadSuspensionOK();
+ final Runnable command =
+ connection.processCommand(this, multipleForksOK);
// TODO (chriswailes): Is this extra check necessary?
if (mIsForkChild) {
// We're in the child. We should always have a command to run at
- // this stage if processOneCommand hasn't called "exec".
+ // this stage if processCommand hasn't called "exec".
if (command == null) {
throw new IllegalStateException("command == null");
}
@@ -565,7 +567,7 @@ class ZygoteServer {
}
// We don't know whether the remote side of the socket was closed or
- // not until we attempt to read from it from processOneCommand. This
+ // not until we attempt to read from it from processCommand. This
// shows up as a regular POLLIN event in our regular processing
// loop.
if (connection.isClosedByPeer()) {
diff --git a/core/java/com/android/server/OWNERS b/core/java/com/android/server/OWNERS
new file mode 100644
index 000000000000..1262925447b9
--- /dev/null
+++ b/core/java/com/android/server/OWNERS
@@ -0,0 +1 @@
+per-file SystemConfig.java = toddke@google.com
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index 0d05a6b82d36..e58ad79de89e 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -206,6 +206,7 @@ cc_library_shared {
"com_android_internal_os_KernelCpuUidBpfMapReader.cpp",
"com_android_internal_os_KernelSingleUidTimeReader.cpp",
"com_android_internal_os_Zygote.cpp",
+ "com_android_internal_os_ZygoteCommandBuffer.cpp",
"com_android_internal_os_ZygoteInit.cpp",
"hwbinder/EphemeralStorage.cpp",
"fd_utils.cpp",
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index 3198cb1b8140..c01f741862b1 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -193,6 +193,7 @@ extern int register_com_android_internal_os_FuseAppLoop(JNIEnv* env);
extern int register_com_android_internal_os_KernelCpuUidBpfMapReader(JNIEnv *env);
extern int register_com_android_internal_os_KernelSingleUidTimeReader(JNIEnv *env);
extern int register_com_android_internal_os_Zygote(JNIEnv *env);
+extern int register_com_android_internal_os_ZygoteCommandBuffer(JNIEnv *env);
extern int register_com_android_internal_os_ZygoteInit(JNIEnv *env);
extern int register_com_android_internal_util_VirtualRefBasePtr(JNIEnv *env);
@@ -1524,6 +1525,7 @@ static const RegJNIRec gRegJNI[] = {
REG_JNI(register_com_android_internal_net_NetworkUtilsInternal),
REG_JNI(register_com_android_internal_os_ClassLoaderFactory),
REG_JNI(register_com_android_internal_os_Zygote),
+ REG_JNI(register_com_android_internal_os_ZygoteCommandBuffer),
REG_JNI(register_com_android_internal_os_ZygoteInit),
REG_JNI(register_com_android_internal_util_VirtualRefBasePtr),
REG_JNI(register_android_hardware_Camera),
diff --git a/core/jni/OWNERS b/core/jni/OWNERS
index 35d1d7bd7946..19c6a625646e 100644
--- a/core/jni/OWNERS
+++ b/core/jni/OWNERS
@@ -42,6 +42,9 @@ per-file android_os_HwParcel* = file:platform/system/libhwbinder:/OWNERS
per-file android_os_HwRemoteBinder* = file:platform/system/libhwbinder:/OWNERS
per-file EphemeralStorage* = file:platform/system/libhwbinder:/OWNERS
+# Sensor
+per-file android_hardware_SensorManager* = arthuri@google.com, bduddie@google.com, stange@google.com
+
per-file *Zygote* = file:/ZYGOTE_OWNERS
per-file Android.bp = file:platform/build/soong:/OWNERS
per-file android_animation_* = file:/core/java/android/animation/OWNERS
diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index 903ecaef4938..4cef2b099589 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -17,6 +17,8 @@
#define LOG_TAG "Zygote"
#define ATRACE_TAG ATRACE_TAG_DALVIK
+#include "com_android_internal_os_Zygote.h"
+
#include <async_safe/log.h>
// sys/mount.h has to come before linux/fs.h due to redefinition of MS_RDONLY, MS_BIND, etc
@@ -91,19 +93,6 @@
#include "nativebridge/native_bridge.h"
-/* Functions in the callchain during the fork shall not be protected with
- Armv8.3-A Pointer Authentication, otherwise child will not be able to return. */
-#ifdef __ARM_FEATURE_PAC_DEFAULT
-#ifdef __ARM_FEATURE_BTI_DEFAULT
-#define NO_PAC_FUNC __attribute__((target("branch-protection=bti")))
-#else
-#define NO_PAC_FUNC __attribute__((target("branch-protection=none")))
-#endif /* __ARM_FEATURE_BTI_DEFAULT */
-#else /* !__ARM_FEATURE_PAC_DEFAULT */
-#define NO_PAC_FUNC
-#endif /* __ARM_FEATURE_PAC_DEFAULT */
-
-
namespace {
// TODO (chriswailes): Add a function to initialize native Zygote data.
@@ -118,8 +107,7 @@ using android::base::StringPrintf;
using android::base::WriteStringToFile;
using android::base::GetBoolProperty;
-#define CREATE_ERROR(...) StringPrintf("%s:%d: ", __FILE__, __LINE__). \
- append(StringPrintf(__VA_ARGS__))
+using android::zygote::ZygoteFailure;
// This type is duplicated in fd_utils.h
typedef const std::function<void(std::string)>& fail_fn_t;
@@ -215,7 +203,7 @@ class UsapTableEntry {
static constexpr EntryStorage INVALID_ENTRY_VALUE = {-1, -1};
std::atomic<EntryStorage> mStorage;
- static_assert(decltype(mStorage)::is_always_lock_free);
+ static_assert(decltype(mStorage)::is_always_lock_free); // Accessed from signal handler.
public:
constexpr UsapTableEntry() : mStorage(INVALID_ENTRY_VALUE) {}
@@ -942,36 +930,6 @@ void SetThreadName(const std::string& thread_name) {
}
/**
- * A failure function used to report fatal errors to the managed runtime. This
- * function is often curried with the process name information and then passed
- * to called functions.
- *
- * @param env Managed runtime environment
- * @param process_name A native representation of the process name
- * @param managed_process_name A managed representation of the process name
- * @param msg The error message to be reported
- */
-[[noreturn]]
-static void ZygoteFailure(JNIEnv* env,
- const char* process_name,
- jstring managed_process_name,
- const std::string& msg) {
- std::unique_ptr<ScopedUtfChars> scoped_managed_process_name_ptr = nullptr;
- if (managed_process_name != nullptr) {
- scoped_managed_process_name_ptr.reset(new ScopedUtfChars(env, managed_process_name));
- if (scoped_managed_process_name_ptr->c_str() != nullptr) {
- process_name = scoped_managed_process_name_ptr->c_str();
- }
- }
-
- const std::string& error_msg =
- (process_name == nullptr) ? msg : StringPrintf("(%s) %s", process_name, msg.c_str());
-
- env->FatalError(error_msg.c_str());
- __builtin_unreachable();
-}
-
-/**
* A helper method for converting managed strings to native strings. A fatal
* error is generated if a problem is encountered in extracting a non-null
* string.
@@ -1098,86 +1056,6 @@ static void PAuthKeyChange(JNIEnv* env) {
#endif
}
-// Utility routine to fork a process from the zygote.
-NO_PAC_FUNC
-static pid_t ForkCommon(JNIEnv* env, bool is_system_server,
- const std::vector<int>& fds_to_close,
- const std::vector<int>& fds_to_ignore,
- bool is_priority_fork) {
- SetSignalHandlers();
-
- // Curry a failure function.
- auto fail_fn = std::bind(ZygoteFailure, env, is_system_server ? "system_server" : "zygote",
- nullptr, _1);
-
- // Temporarily block SIGCHLD during forks. The SIGCHLD handler might
- // log, which would result in the logging FDs we close being reopened.
- // This would cause failures because the FDs are not allowlisted.
- //
- // Note that the zygote process is single threaded at this point.
- BlockSignal(SIGCHLD, fail_fn);
-
- // Close any logging related FDs before we start evaluating the list of
- // file descriptors.
- __android_log_close();
- AStatsSocket_close();
-
- // If this is the first fork for this zygote, create the open FD table. If
- // it isn't, we just need to check whether the list of open files has changed
- // (and it shouldn't in the normal case).
- if (gOpenFdTable == nullptr) {
- gOpenFdTable = FileDescriptorTable::Create(fds_to_ignore, fail_fn);
- } else {
- gOpenFdTable->Restat(fds_to_ignore, fail_fn);
- }
-
- android_fdsan_error_level fdsan_error_level = android_fdsan_get_error_level();
-
- // Purge unused native memory in an attempt to reduce the amount of false
- // sharing with the child process. By reducing the size of the libc_malloc
- // region shared with the child process we reduce the number of pages that
- // transition to the private-dirty state when malloc adjusts the meta-data
- // on each of the pages it is managing after the fork.
- mallopt(M_PURGE, 0);
-
- pid_t pid = fork();
-
- if (pid == 0) {
- if (is_priority_fork) {
- setpriority(PRIO_PROCESS, 0, PROCESS_PRIORITY_MAX);
- } else {
- setpriority(PRIO_PROCESS, 0, PROCESS_PRIORITY_MIN);
- }
-
- // The child process.
- PAuthKeyChange(env);
- PreApplicationInit();
-
- // Clean up any descriptors which must be closed immediately
- DetachDescriptors(env, fds_to_close, fail_fn);
-
- // Invalidate the entries in the USAP table.
- ClearUsapTable();
-
- // Re-open all remaining open file descriptors so that they aren't shared
- // with the zygote across a fork.
- gOpenFdTable->ReopenOrDetach(fail_fn);
-
- // Turn fdsan back on.
- android_fdsan_set_error_level(fdsan_error_level);
-
- // Reset the fd to the unsolicited zygote socket
- gSystemServerSocketFd = -1;
- } else {
- ALOGD("Forked child process %d", pid);
- }
-
- // We blocked SIGCHLD prior to a fork, we unblock it here.
- UnblockSignal(SIGCHLD, fail_fn);
-
- return pid;
-}
-
// Create an app data directory over tmpfs overlayed CE / DE storage, and bind mount it
// from the actual app data directory in data mirror.
static bool createAndMountAppData(std::string_view package_name,
@@ -1993,9 +1871,10 @@ static void AddUsapTableEntry(pid_t usap_pid, int read_pipe_fd) {
static int sUsapTableInsertIndex = 0;
int search_index = sUsapTableInsertIndex;
-
do {
if (gUsapTable[search_index].SetIfInvalid(usap_pid, read_pipe_fd)) {
+ ++gUsapPoolCount;
+
// Start our next search right after where we finished this one.
sUsapTableInsertIndex = (search_index + 1) % gUsapTable.size();
@@ -2013,7 +1892,7 @@ static void AddUsapTableEntry(pid_t usap_pid, int read_pipe_fd) {
/**
* Invalidates the entry in the USAPTable corresponding to the provided
* process ID if it is present. If an entry was removed the USAP pool
- * count is decremented.
+ * count is decremented. May be called from signal handler.
*
* @param usap_pid Process ID of the USAP entry to invalidate
* @return True if an entry was invalidated; false otherwise
@@ -2089,6 +1968,121 @@ static void UnmountStorageOnInit(JNIEnv* env) {
namespace android {
+/**
+ * A failure function used to report fatal errors to the managed runtime. This
+ * function is often curried with the process name information and then passed
+ * to called functions.
+ *
+ * @param env Managed runtime environment
+ * @param process_name A native representation of the process name
+ * @param managed_process_name A managed representation of the process name
+ * @param msg The error message to be reported
+ */
+[[noreturn]]
+void zygote::ZygoteFailure(JNIEnv* env,
+ const char* process_name,
+ jstring managed_process_name,
+ const std::string& msg) {
+ std::unique_ptr<ScopedUtfChars> scoped_managed_process_name_ptr = nullptr;
+ if (managed_process_name != nullptr) {
+ scoped_managed_process_name_ptr.reset(new ScopedUtfChars(env, managed_process_name));
+ if (scoped_managed_process_name_ptr->c_str() != nullptr) {
+ process_name = scoped_managed_process_name_ptr->c_str();
+ }
+ }
+
+ const std::string& error_msg =
+ (process_name == nullptr || process_name[0] == '\0') ?
+ msg : StringPrintf("(%s) %s", process_name, msg.c_str());
+
+ env->FatalError(error_msg.c_str());
+ __builtin_unreachable();
+}
+
+// Utility routine to fork a process from the zygote.
+NO_PAC_FUNC
+pid_t zygote::ForkCommon(JNIEnv* env, bool is_system_server,
+ const std::vector<int>& fds_to_close,
+ const std::vector<int>& fds_to_ignore,
+ bool is_priority_fork,
+ bool purge) {
+ SetSignalHandlers();
+
+ // Curry a failure function.
+ auto fail_fn = std::bind(zygote::ZygoteFailure, env,
+ is_system_server ? "system_server" : "zygote",
+ nullptr, _1);
+
+ // Temporarily block SIGCHLD during forks. The SIGCHLD handler might
+ // log, which would result in the logging FDs we close being reopened.
+ // This would cause failures because the FDs are not allowlisted.
+ //
+ // Note that the zygote process is single threaded at this point.
+ BlockSignal(SIGCHLD, fail_fn);
+
+ // Close any logging related FDs before we start evaluating the list of
+ // file descriptors.
+ __android_log_close();
+ AStatsSocket_close();
+
+ // If this is the first fork for this zygote, create the open FD table. If
+ // it isn't, we just need to check whether the list of open files has changed
+ // (and it shouldn't in the normal case).
+ if (gOpenFdTable == nullptr) {
+ gOpenFdTable = FileDescriptorTable::Create(fds_to_ignore, fail_fn);
+ } else {
+ gOpenFdTable->Restat(fds_to_ignore, fail_fn);
+ }
+
+ android_fdsan_error_level fdsan_error_level = android_fdsan_get_error_level();
+
+ if (purge) {
+ // Purge unused native memory in an attempt to reduce the amount of false
+ // sharing with the child process. By reducing the size of the libc_malloc
+ // region shared with the child process we reduce the number of pages that
+ // transition to the private-dirty state when malloc adjusts the meta-data
+ // on each of the pages it is managing after the fork.
+ mallopt(M_PURGE, 0);
+ }
+
+ pid_t pid = fork();
+
+ if (pid == 0) {
+ if (is_priority_fork) {
+ setpriority(PRIO_PROCESS, 0, PROCESS_PRIORITY_MAX);
+ } else {
+ setpriority(PRIO_PROCESS, 0, PROCESS_PRIORITY_MIN);
+ }
+
+ // The child process.
+ PAuthKeyChange(env);
+ PreApplicationInit();
+
+ // Clean up any descriptors which must be closed immediately
+ DetachDescriptors(env, fds_to_close, fail_fn);
+
+ // Invalidate the entries in the USAP table.
+ ClearUsapTable();
+
+ // Re-open all remaining open file descriptors so that they aren't shared
+ // with the zygote across a fork.
+ gOpenFdTable->ReopenOrDetach(fail_fn);
+
+ // Turn fdsan back on.
+ android_fdsan_set_error_level(fdsan_error_level);
+
+ // Reset the fd to the unsolicited zygote socket
+ gSystemServerSocketFd = -1;
+ } else {
+ ALOGD("Forked child process %d", pid);
+ }
+
+ // We blocked SIGCHLD prior to a fork, we unblock it here.
+ UnblockSignal(SIGCHLD, fail_fn);
+
+ return pid;
+}
+
static void com_android_internal_os_Zygote_nativePreApplicationInit(JNIEnv*, jclass) {
PreApplicationInit();
}
@@ -2105,7 +2099,8 @@ static jint com_android_internal_os_Zygote_nativeForkAndSpecialize(
jlong capabilities = CalculateCapabilities(env, uid, gid, gids, is_child_zygote);
if (UNLIKELY(managed_fds_to_close == nullptr)) {
- ZygoteFailure(env, "zygote", nice_name, "Zygote received a null fds_to_close vector.");
+ zygote::ZygoteFailure(env, "zygote", nice_name,
+ "Zygote received a null fds_to_close vector.");
}
std::vector<int> fds_to_close =
@@ -2131,7 +2126,7 @@ static jint com_android_internal_os_Zygote_nativeForkAndSpecialize(
fds_to_ignore.push_back(gSystemServerSocketFd);
}
- pid_t pid = ForkCommon(env, false, fds_to_close, fds_to_ignore, true);
+ pid_t pid = zygote::ForkCommon(env, false, fds_to_close, fds_to_ignore, true);
if (pid == 0) {
SpecializeCommon(env, uid, gid, gids, runtime_flags, rlimits,
@@ -2166,10 +2161,10 @@ static jint com_android_internal_os_Zygote_nativeForkSystemServer(
fds_to_ignore.push_back(gSystemServerSocketFd);
}
- pid_t pid = ForkCommon(env, true,
- fds_to_close,
- fds_to_ignore,
- true);
+ pid_t pid = zygote::ForkCommon(env, true,
+ fds_to_close,
+ fds_to_ignore,
+ true);
if (pid == 0) {
// System server prcoess does not need data isolation so no need to
// know pkg_data_info_list.
@@ -2209,58 +2204,74 @@ static jint com_android_internal_os_Zygote_nativeForkSystemServer(
* ensuring proper file descriptor hygiene.
*
* @param env Managed runtime environment
- * @param read_pipe_fd The read FD for the USAP reporting pipe. Manually closed by blastlas
- * in managed code.
+ * @param read_pipe_fd The read FD for the USAP reporting pipe. Manually closed by the child
+ * in managed code. -1 indicates none.
* @param write_pipe_fd The write FD for the USAP reporting pipe. Manually closed by the
- * zygote in managed code.
+ * zygote in managed code. -1 indicates none.
* @param managed_session_socket_fds A list of anonymous session sockets that must be ignored by
* the FD hygiene code and automatically "closed" in the new USAP.
+ * @param args_known Arguments for specialization are available; no need to read from a socket
* @param is_priority_fork Controls the nice level assigned to the newly created process
- * @return
+ * @return child pid in the parent, 0 in the child
*/
NO_PAC_FUNC
-static jint com_android_internal_os_Zygote_nativeForkUsap(JNIEnv* env,
- jclass,
- jint read_pipe_fd,
- jint write_pipe_fd,
- jintArray managed_session_socket_fds,
- jboolean is_priority_fork) {
- std::vector<int> fds_to_close(MakeUsapPipeReadFDVector()),
- fds_to_ignore(fds_to_close);
-
+static jint com_android_internal_os_Zygote_nativeForkApp(JNIEnv* env,
+ jclass,
+ jint read_pipe_fd,
+ jint write_pipe_fd,
+ jintArray managed_session_socket_fds,
+ jboolean args_known,
+ jboolean is_priority_fork) {
std::vector<int> session_socket_fds =
ExtractJIntArray(env, "USAP", nullptr, managed_session_socket_fds)
.value_or(std::vector<int>());
+ return zygote::forkApp(env, read_pipe_fd, write_pipe_fd, session_socket_fds,
+ args_known == JNI_TRUE, is_priority_fork == JNI_TRUE, true);
+}
- // The USAP Pool Event FD is created during the initialization of the
- // USAP pool and should always be valid here.
+NO_PAC_FUNC
+int zygote::forkApp(JNIEnv* env,
+ int read_pipe_fd,
+ int write_pipe_fd,
+ const std::vector<int>& session_socket_fds,
+ bool args_known,
+ bool is_priority_fork,
+ bool purge) {
+
+ std::vector<int> fds_to_close(MakeUsapPipeReadFDVector()),
+ fds_to_ignore(fds_to_close);
fds_to_close.push_back(gZygoteSocketFD);
- fds_to_close.push_back(gUsapPoolEventFD);
- fds_to_close.insert(fds_to_close.end(), session_socket_fds.begin(), session_socket_fds.end());
if (gSystemServerSocketFd != -1) {
fds_to_close.push_back(gSystemServerSocketFd);
}
+ if (args_known) {
+ fds_to_close.push_back(gUsapPoolSocketFD);
+ }
+ fds_to_close.insert(fds_to_close.end(), session_socket_fds.begin(), session_socket_fds.end());
- fds_to_ignore.push_back(gZygoteSocketFD);
fds_to_ignore.push_back(gUsapPoolSocketFD);
- fds_to_ignore.push_back(gUsapPoolEventFD);
- fds_to_ignore.push_back(read_pipe_fd);
- fds_to_ignore.push_back(write_pipe_fd);
+ fds_to_ignore.push_back(gZygoteSocketFD);
+ if (read_pipe_fd != -1) {
+ fds_to_ignore.push_back(read_pipe_fd);
+ }
+ if (write_pipe_fd != -1) {
+ fds_to_ignore.push_back(write_pipe_fd);
+ }
fds_to_ignore.insert(fds_to_ignore.end(), session_socket_fds.begin(), session_socket_fds.end());
+
+ if (gUsapPoolEventFD != -1) {
+ fds_to_close.push_back(gUsapPoolEventFD);
+ fds_to_ignore.push_back(gUsapPoolEventFD);
+ }
if (gSystemServerSocketFd != -1) {
+ if (args_known) {
+ fds_to_close.push_back(gSystemServerSocketFd);
+ }
fds_to_ignore.push_back(gSystemServerSocketFd);
}
-
- pid_t usap_pid = ForkCommon(env, /* is_system_server= */ false, fds_to_close, fds_to_ignore,
- is_priority_fork == JNI_TRUE);
-
- if (usap_pid != 0) {
- ++gUsapPoolCount;
- AddUsapTableEntry(usap_pid, read_pipe_fd);
- }
-
- return usap_pid;
+ return zygote::ForkCommon(env, /* is_system_server= */ false, fds_to_close,
+ fds_to_ignore, is_priority_fork == JNI_TRUE, purge);
}
static void com_android_internal_os_Zygote_nativeAllowFileAcrossFork(
@@ -2374,7 +2385,7 @@ static void com_android_internal_os_Zygote_nativeInitNativeState(JNIEnv* env, jc
*/
if (!SetTaskProfiles(0, {})) {
- ZygoteFailure(env, "zygote", nullptr, "Zygote SetTaskProfiles failed");
+ zygote::ZygoteFailure(env, "zygote", nullptr, "Zygote SetTaskProfiles failed");
}
}
@@ -2392,15 +2403,21 @@ static jintArray com_android_internal_os_Zygote_nativeGetUsapPipeFDs(JNIEnv* env
return managed_usap_fds;
}
+/*
+ * Add the given pid and file descriptor to the Usap table. CriticalNative method.
+ */
+static void com_android_internal_os_Zygote_nativeAddUsapTableEntry(jint pid, jint read_pipe_fd) {
+ AddUsapTableEntry(pid, read_pipe_fd);
+}
+
/**
- * A JNI wrapper around RemoveUsapTableEntry.
+ * A JNI wrapper around RemoveUsapTableEntry. CriticalNative method.
*
* @param env Managed runtime environment
* @param usap_pid Process ID of the USAP entry to invalidate
* @return True if an entry was invalidated; false otherwise.
*/
-static jboolean com_android_internal_os_Zygote_nativeRemoveUsapTableEntry(JNIEnv* env, jclass,
- jint usap_pid) {
+static jboolean com_android_internal_os_Zygote_nativeRemoveUsapTableEntry(jint usap_pid) {
return RemoveUsapTableEntry(usap_pid);
}
@@ -2415,7 +2432,8 @@ static jboolean com_android_internal_os_Zygote_nativeRemoveUsapTableEntry(JNIEnv
static jint com_android_internal_os_Zygote_nativeGetUsapPoolEventFD(JNIEnv* env, jclass) {
if (gUsapPoolEventFD == -1) {
if ((gUsapPoolEventFD = eventfd(0, 0)) == -1) {
- ZygoteFailure(env, "zygote", nullptr, StringPrintf("Unable to create eventfd: %s", strerror(errno)));
+ zygote::ZygoteFailure(env, "zygote", nullptr,
+ StringPrintf("Unable to create eventfd: %s", strerror(errno)));
}
}
@@ -2458,12 +2476,12 @@ static void com_android_internal_os_Zygote_nativeEmptyUsapPool(JNIEnv* env, jcla
}
static void com_android_internal_os_Zygote_nativeBlockSigTerm(JNIEnv* env, jclass) {
- auto fail_fn = std::bind(ZygoteFailure, env, "usap", nullptr, _1);
+ auto fail_fn = std::bind(zygote::ZygoteFailure, env, "usap", nullptr, _1);
BlockSignal(SIGTERM, fail_fn);
}
static void com_android_internal_os_Zygote_nativeUnblockSigTerm(JNIEnv* env, jclass) {
- auto fail_fn = std::bind(ZygoteFailure, env, "usap", nullptr, _1);
+ auto fail_fn = std::bind(zygote::ZygoteFailure, env, "usap", nullptr, _1);
UnblockSignal(SIGTERM, fail_fn);
}
@@ -2569,7 +2587,10 @@ static const JNINativeMethod gMethods[] = {
(void*)com_android_internal_os_Zygote_nativePreApplicationInit},
{"nativeInstallSeccompUidGidFilter", "(II)V",
(void*)com_android_internal_os_Zygote_nativeInstallSeccompUidGidFilter},
- {"nativeForkUsap", "(II[IZ)I", (void*)com_android_internal_os_Zygote_nativeForkUsap},
+ {"nativeForkApp", "(II[IZZ)I", (void*)com_android_internal_os_Zygote_nativeForkApp},
+ // @CriticalNative
+ {"nativeAddUsapTableEntry", "(II)V",
+ (void*)com_android_internal_os_Zygote_nativeAddUsapTableEntry},
{"nativeSpecializeAppProcess",
"(II[II[[IILjava/lang/String;Ljava/lang/String;ZLjava/lang/String;Ljava/lang/"
"String;Z[Ljava/lang/String;[Ljava/lang/String;ZZ)V",
@@ -2578,6 +2599,10 @@ static const JNINativeMethod gMethods[] = {
(void*)com_android_internal_os_Zygote_nativeInitNativeState},
{"nativeGetUsapPipeFDs", "()[I",
(void*)com_android_internal_os_Zygote_nativeGetUsapPipeFDs},
+ // @CriticalNative
+ {"nativeAddUsapTableEntry", "(II)V",
+ (void*)com_android_internal_os_Zygote_nativeAddUsapTableEntry},
+ // @CriticalNative
{"nativeRemoveUsapTableEntry", "(I)Z",
(void*)com_android_internal_os_Zygote_nativeRemoveUsapTableEntry},
{"nativeGetUsapPoolEventFD", "()I",
diff --git a/core/jni/com_android_internal_os_Zygote.h b/core/jni/com_android_internal_os_Zygote.h
new file mode 100644
index 000000000000..d2da91476bc7
--- /dev/null
+++ b/core/jni/com_android_internal_os_Zygote.h
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2008 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.
+ */
+
+#ifndef _COM_ANDROID_INTERNAL_OS_ZYGOTE_H
+#define _COM_ANDROID_INTERNAL_OS_ZYGOTE_H
+
+#define LOG_TAG "Zygote"
+#define ATRACE_TAG ATRACE_TAG_DALVIK
+
+/* Functions in the callchain during the fork shall not be protected with
+ Armv8.3-A Pointer Authentication, otherwise child will not be able to return. */
+#ifdef __ARM_FEATURE_PAC_DEFAULT
+#ifdef __ARM_FEATURE_BTI_DEFAULT
+#define NO_PAC_FUNC __attribute__((target("branch-protection=bti")))
+#else
+#define NO_PAC_FUNC __attribute__((target("branch-protection=none")))
+#endif /* __ARM_FEATURE_BTI_DEFAULT */
+#else /* !__ARM_FEATURE_PAC_DEFAULT */
+#define NO_PAC_FUNC
+#endif /* __ARM_FEATURE_PAC_DEFAULT */
+
+#include <jni.h>
+#include <vector>
+#include <android-base/stringprintf.h>
+
+#define CREATE_ERROR(...) StringPrintf("%s:%d: ", __FILE__, __LINE__). \
+ append(StringPrintf(__VA_ARGS__))
+
+namespace android {
+namespace zygote {
+
+NO_PAC_FUNC
+pid_t ForkCommon(JNIEnv* env,bool is_system_server,
+ const std::vector<int>& fds_to_close,
+ const std::vector<int>& fds_to_ignore,
+ bool is_priority_fork,
+ bool purge = true);
+
+/**
+ * Fork a process. The pipe fds are used for usap communication, or -1 in
+ * other cases. Session_socket_fds are FDs used for zygote communication that must be dealt
+ * with hygienically, but are not otherwise used here. Args_known indicates that the process
+ * will be immediately specialized with arguments that are already known, so no usap
+ * communication is required. Is_priority_fork should be true if this is on the app startup
+ * critical path. Purge specifies that unused pages should be purged before the fork.
+ */
+NO_PAC_FUNC
+int forkApp(JNIEnv* env,
+ int read_pipe_fd,
+ int write_pipe_fd,
+ const std::vector<int>& session_socket_fds,
+ bool args_known,
+ bool is_priority_fork,
+ bool purge);
+
+[[noreturn]]
+void ZygoteFailure(JNIEnv* env,
+ const char* process_name,
+ jstring managed_process_name,
+ const std::string& msg);
+
+} // namespace zygote
+} // namespace android
+
+#endif // _COM_ANDROID_INTERNAL_OS_ZYGOTE_
diff --git a/core/jni/com_android_internal_os_ZygoteCommandBuffer.cpp b/core/jni/com_android_internal_os_ZygoteCommandBuffer.cpp
new file mode 100644
index 000000000000..011e8f8f1b8c
--- /dev/null
+++ b/core/jni/com_android_internal_os_ZygoteCommandBuffer.cpp
@@ -0,0 +1,512 @@
+/*
+ * 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.
+ */
+
+#include "com_android_internal_os_Zygote.h"
+
+#include <algorithm>
+#include <android-base/logging.h>
+#include <async_safe/log.h>
+#include <cctype>
+#include <chrono>
+#include <core_jni_helpers.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <jni.h>
+#include <nativehelper/JNIHelp.h>
+#include <optional>
+#include <poll.h>
+#include <unistd.h>
+#include <utility>
+#include <utils/misc.h>
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <vector>
+
+namespace android {
+
+using namespace std::placeholders;
+using android::base::StringPrintf;
+using android::zygote::ZygoteFailure;
+
+// WARNING: Knows a little about the wire protocol used to communicate with Zygote.
+// TODO: Fix error handling.
+
+constexpr size_t MAX_COMMAND_BYTES = 12200;
+constexpr size_t NICE_NAME_BYTES = 50;
+
+// A buffer optionally bundled with a file descriptor from which we can fill it.
+// Does not own the file descriptor; destroying a NativeCommandBuffer does not
+// close the descriptor.
+class NativeCommandBuffer {
+ public:
+ NativeCommandBuffer(int sourceFd): mEnd(0), mNext(0), mLinesLeft(0), mFd(sourceFd) {}
+
+ // Read mNext line from mFd, filling mBuffer from file descriptor, as needed.
+ // Return a pair of pointers pointing to the first character, and one past the
+ // mEnd of the line, i.e. at the newline. Returns nothing on failure.
+ template<class FailFn>
+ std::optional<std::pair<char*, char*>> readLine(FailFn fail_fn) {
+ char* result = mBuffer + mNext;
+ while (true) {
+ if (mNext == mEnd) {
+ if (mEnd == MAX_COMMAND_BYTES) {
+ return {};
+ }
+ if (mFd == -1) {
+ fail_fn("ZygoteCommandBuffer.readLine attempted to read from mFd -1");
+ }
+ ssize_t nread = TEMP_FAILURE_RETRY(read(mFd, mBuffer + mEnd, MAX_COMMAND_BYTES - mEnd));
+ if (nread <= 0) {
+ if (nread == 0) {
+ return {};
+ }
+ fail_fn(CREATE_ERROR("session socket read failed: %s", strerror(errno)));
+ } else if (nread == MAX_COMMAND_BYTES - mEnd) {
+ // This is pessimistic by one character, but close enough.
+ fail_fn("ZygoteCommandBuffer overflowed: command too long");
+ }
+ mEnd += nread;
+ }
+ // UTF-8 does not allow newline to occur as part of a multibyte character.
+ char* nl = static_cast<char *>(memchr(mBuffer + mNext, '\n', mEnd - mNext));
+ if (nl == nullptr) {
+ mNext = mEnd;
+ } else {
+ mNext = nl - mBuffer + 1;
+ if (--mLinesLeft < 0) {
+ fail_fn("ZygoteCommandBuffer.readLine attempted to read past mEnd of command");
+ }
+ return std::make_pair(result, nl);
+ }
+ }
+ }
+
+ void reset() {
+ mNext = 0;
+ }
+
+ // Make sure the current command is fully buffered, without reading past the current command.
+ template<class FailFn>
+ void readAllLines(FailFn fail_fn) {
+ while (mLinesLeft > 0) {
+ readLine(fail_fn);
+ }
+ }
+
+ void clear() {
+ // Don't bother to actually clear the buffer; it'll be unmapped in the child anyway.
+ reset();
+ mNiceName[0] = '\0';
+ mEnd = 0;
+ }
+
+ // Insert line into the mBuffer. Checks that the mBuffer is not associated with an mFd.
+ // Implicitly adds newline separators. Allows mBuffer contents to be explicitly set.
+ void insert(const char* line, size_t lineLen) {
+ DCHECK(mFd == -1);
+ CHECK(mEnd + lineLen < MAX_COMMAND_BYTES);
+ strncpy(mBuffer + mEnd, line, lineLen);
+ mBuffer[mEnd + lineLen] = '\n';
+ mEnd += lineLen + 1;
+ }
+
+ // Clear mBuffer, start reading new command, return the number of arguments, leaving mBuffer
+ // positioned at the beginning of first argument. Return 0 on EOF.
+ template<class FailFn>
+ int getCount(FailFn fail_fn) {
+ mLinesLeft = 1;
+ auto line = readLine(fail_fn);
+ if (!line.has_value()) {
+ return 0;
+ }
+ char* countString = line.value().first; // Newline terminated.
+ long nArgs = atol(countString);
+ if (nArgs <= 0 || nArgs >= MAX_COMMAND_BYTES / 2) {
+ fail_fn(CREATE_ERROR("Unreasonable argument count %ld", nArgs));
+ }
+ mLinesLeft = nArgs;
+ return static_cast<int>(nArgs);
+ }
+
+ // Is the mBuffer a simple fork command?
+ // We disallow request to wrap the child process, child zygotes, anything that
+ // mentions capabilities or requests uid < minUid.
+ // We insist that --setuid and --setgid arguments are explicitly included and that the
+ // command starts with --runtime-args.
+ // Assumes we are positioned at the beginning of the command after the argument count,
+ // and leaves the position at some indeterminate position in the buffer.
+ // As a side effect, this sets mNiceName to a non-empty string, if possible.
+ template<class FailFn>
+ bool isSimpleForkCommand(int minUid, FailFn fail_fn) {
+ if (mLinesLeft <= 0 || mLinesLeft >= MAX_COMMAND_BYTES / 2) {
+ return false;
+ }
+ static const char* RUNTIME_ARGS = "--runtime-args";
+ static const char* INVOKE_WITH = "--invoke-with";
+ static const char* CHILD_ZYGOTE = "--start-child-zygote";
+ static const char* SETUID = "--setuid=";
+ static const char* SETGID = "--setgid=";
+ static const char* CAPABILITIES = "--capabilities";
+ static const char* NICE_NAME = "--nice-name=";
+ static const size_t RA_LENGTH = strlen(RUNTIME_ARGS);
+ static const size_t IW_LENGTH = strlen(INVOKE_WITH);
+ static const size_t CZ_LENGTH = strlen(CHILD_ZYGOTE);
+ static const size_t SU_LENGTH = strlen(SETUID);
+ static const size_t SG_LENGTH = strlen(SETGID);
+ static const size_t CA_LENGTH = strlen(CAPABILITIES);
+ static const size_t NN_LENGTH = strlen(NICE_NAME);
+
+ bool saw_setuid = false, saw_setgid = false;
+ bool saw_runtime_args = false;
+
+ while (mLinesLeft > 0) {
+ auto read_result = readLine(fail_fn);
+ if (!read_result.has_value()) {
+ return false;
+ }
+ auto [arg_start, arg_end] = read_result.value();
+ if (arg_end - arg_start == RA_LENGTH
+ && strncmp(arg_start, RUNTIME_ARGS, RA_LENGTH) == 0) {
+ saw_runtime_args = true;
+ continue;
+ }
+ if (arg_end - arg_start >= NN_LENGTH
+ && strncmp(arg_start, NICE_NAME, NN_LENGTH) == 0) {
+ size_t name_len = arg_end - (arg_start + NN_LENGTH);
+ size_t copy_len = std::min(name_len, NICE_NAME_BYTES - 1);
+ memcpy(mNiceName, arg_start + NN_LENGTH, copy_len);
+ mNiceName[copy_len] = '\0';
+ continue;
+ }
+ if (arg_end - arg_start == IW_LENGTH
+ && strncmp(arg_start, INVOKE_WITH, IW_LENGTH) == 0) {
+ // This also removes the need for invoke-with security checks here.
+ return false;
+ }
+ if (arg_end - arg_start == CZ_LENGTH
+ && strncmp(arg_start, CHILD_ZYGOTE, CZ_LENGTH) == 0) {
+ return false;
+ }
+ if (arg_end - arg_start >= CA_LENGTH
+ && strncmp(arg_start, CAPABILITIES, CA_LENGTH) == 0) {
+ return false;
+ }
+ if (arg_end - arg_start >= SU_LENGTH
+ && strncmp(arg_start, SETUID, SU_LENGTH) == 0) {
+ int uid = digitsVal(arg_start + SU_LENGTH, arg_end);
+ if (uid < minUid) {
+ return false;
+ }
+ saw_setuid = true;
+ continue;
+ }
+ if (arg_end - arg_start >= SG_LENGTH
+ && strncmp(arg_start, SETGID, SG_LENGTH) == 0) {
+ int gid = digitsVal(arg_start + SG_LENGTH, arg_end);
+ if (gid == -1) {
+ return false;
+ }
+ saw_setgid = true;
+ }
+ }
+ return saw_runtime_args && saw_setuid && saw_setgid;
+ }
+
+ void setFd(int new_fd) {
+ mFd = new_fd;
+ }
+
+ int getFd() const {
+ return mFd;
+ }
+
+ const char* niceNameAddr() const {
+ return mNiceName;
+ }
+
+ // Debug only:
+ void logState() const {
+ ALOGD("mbuffer starts with %c%c, nice name is %s, "
+ "mEnd = %u, mNext = %u, mLinesLeft = %d, mFd = %d",
+ mBuffer[0], (mBuffer[1] == '\n' ? ' ' : mBuffer[1]),
+ niceNameAddr(),
+ static_cast<unsigned>(mEnd), static_cast<unsigned>(mNext),
+ static_cast<int>(mLinesLeft), mFd);
+ }
+
+ private:
+ // Picky version of atoi(). No sign or unexpected characters allowed. Return -1 on failure.
+ static int digitsVal(char* start, char* end) {
+ int result = 0;
+ if (end - start > 6) {
+ return -1;
+ }
+ for (char* dp = start; dp < end; ++dp) {
+ if (*dp < '0' || *dp > '9') {
+ ALOGW("Argument failed integer format check");
+ return -1;
+ }
+ result = 10 * result + (*dp - '0');
+ }
+ return result;
+ }
+
+ uint32_t mEnd; // Index of first empty byte in the mBuffer.
+ uint32_t mNext; // Index of first character past last line returned by readLine.
+ int32_t mLinesLeft; // Lines in current command that haven't yet been read.
+ int mFd; // Open file descriptor from which we can read more. -1 if none.
+ char mNiceName[NICE_NAME_BYTES];
+ char mBuffer[MAX_COMMAND_BYTES];
+};
+
+static_assert(sizeof(NativeCommandBuffer) < 3 * 4096);
+
+static int buffersAllocd(0);
+
+// Get a new NativeCommandBuffer. Can only be called once between freeNativeBuffer calls,
+// so that only one buffer exists at a time.
+jlong com_android_internal_os_ZygoteCommandBuffer_getNativeBuffer(JNIEnv* env, jclass, jint fd) {
+ CHECK(buffersAllocd == 0);
+ ++buffersAllocd;
+ // MMap explicitly to get it page aligned.
+ void *bufferMem = mmap(NULL, sizeof(NativeCommandBuffer), PROT_READ | PROT_WRITE,
+ MAP_ANONYMOUS | MAP_PRIVATE | MAP_POPULATE, -1, 0);
+ // Currently we mmap and unmap one for every request handled by the Java code.
+ // That could be improved, but unclear it matters.
+ if (bufferMem == MAP_FAILED) {
+ ZygoteFailure(env, nullptr, nullptr, "Failed to map argument buffer");
+ }
+ return (jlong) new(bufferMem) NativeCommandBuffer(fd);
+}
+
+// Delete native command buffer.
+void com_android_internal_os_ZygoteCommandBuffer_freeNativeBuffer(JNIEnv* env, jclass,
+ jlong j_buffer) {
+ CHECK(buffersAllocd == 1);
+ NativeCommandBuffer* n_buffer = reinterpret_cast<NativeCommandBuffer*>(j_buffer);
+ n_buffer->~NativeCommandBuffer();
+ if (munmap(n_buffer, sizeof(NativeCommandBuffer)) != 0) {
+ ZygoteFailure(env, nullptr, nullptr, "Failed to unmap argument buffer");
+ }
+ --buffersAllocd;
+}
+
+// Clear the buffer, read the line containing the count, and return the count.
+jint com_android_internal_os_ZygoteCommandBuffer_nativeGetCount(JNIEnv* env, jclass,
+ jlong j_buffer) {
+ NativeCommandBuffer* n_buffer = reinterpret_cast<NativeCommandBuffer*>(j_buffer);
+ auto fail_fn = std::bind(ZygoteFailure, env, nullptr, nullptr, _1);
+ return n_buffer->getCount(fail_fn);
+}
+
+// Explicitly insert a string as the last line (argument) of the buffer.
+void com_android_internal_os_ZygoteCommandBuffer_insert(JNIEnv* env, jclass, jlong j_buffer,
+ jstring line) {
+ NativeCommandBuffer* n_buffer = reinterpret_cast<NativeCommandBuffer*>(j_buffer);
+ size_t lineLen = static_cast<size_t>(env->GetStringUTFLength(line));
+ const char* cstring = env->GetStringUTFChars(line, NULL);
+ n_buffer->insert(cstring, lineLen);
+ env->ReleaseStringUTFChars(line, cstring);
+}
+
+// Read a line from the buffer, refilling as necessary.
+jstring com_android_internal_os_ZygoteCommandBuffer_nativeNextArg(JNIEnv* env, jclass,
+ jlong j_buffer) {
+ NativeCommandBuffer* n_buffer = reinterpret_cast<NativeCommandBuffer*>(j_buffer);
+ auto fail_fn = std::bind(ZygoteFailure, env, n_buffer->niceNameAddr(), nullptr, _1);
+ auto line = n_buffer->readLine(fail_fn);
+ if (!line.has_value()) {
+ fail_fn("Incomplete zygote command");
+ }
+ auto [cresult, endp] = line.value();
+ // OK to temporarily clobber the buffer, since this is not thread safe, and we're modifying
+ // the buffer anyway.
+ *endp = '\0';
+ jstring result = env->NewStringUTF(cresult);
+ *endp = '\n';
+ return result;
+}
+
+// Read all lines from the current command into the buffer, and then reset the buffer, so
+// we will start reading again at the beginning of the command, starting with the argument
+// count. And we don't need access to the fd to do so.
+void com_android_internal_os_ZygoteCommandBuffer_nativeReadFullyAndReset(JNIEnv* env, jclass,
+ jlong j_buffer) {
+ NativeCommandBuffer* n_buffer = reinterpret_cast<NativeCommandBuffer*>(j_buffer);
+ auto fail_fn = std::bind(ZygoteFailure, env, n_buffer->niceNameAddr(), nullptr, _1);
+ n_buffer->readAllLines(fail_fn);
+ n_buffer->reset();
+}
+
+// Fork a child as specified by the current command buffer, and refill the command
+// buffer from the given socket. So long as the result is another simple fork command,
+// repeat this process.
+// It must contain a fork command, which is currently restricted not to fork another
+// zygote or involve a wrapper process.
+// The initial buffer should be partially or entirely read; we read it fully and reset it.
+// When we return, the buffer contains the command we couldn't handle, and has been reset().
+// We return false in the parent when we see a command we didn't understand, and thus the
+// command in the buffer still needs to be executed.
+// We return true in each child.
+// We only process fork commands if the peer uid matches expected_uid.
+// For every fork command after the first, we check that the requested uid is at
+// least minUid.
+NO_PAC_FUNC
+jboolean com_android_internal_os_ZygoteCommandBuffer_nativeForkRepeatedly(
+ JNIEnv* env,
+ jclass,
+ jlong j_buffer,
+ jint zygote_socket_fd,
+ jint expected_uid,
+ jint minUid,
+ jstring managed_nice_name) {
+
+ NativeCommandBuffer* n_buffer = reinterpret_cast<NativeCommandBuffer*>(j_buffer);
+ int session_socket = n_buffer->getFd();
+ std::vector<int> session_socket_fds {session_socket};
+ auto fail_fn_1 = std::bind(ZygoteFailure, env, static_cast<const char*>(nullptr),
+ static_cast<jstring>(managed_nice_name), _1);
+ // This binds to the nice name address; the actual names are updated by isSimpleForkCommand:
+ auto fail_fn_n = std::bind(ZygoteFailure, env, n_buffer->niceNameAddr(),
+ static_cast<jstring>(nullptr), _1);
+ auto fail_fn_z = std::bind(ZygoteFailure, env, "zygote", nullptr, _1);
+
+ struct pollfd fd_structs[2];
+ static const int ZYGOTE_IDX = 0;
+ static const int SESSION_IDX = 1;
+ fd_structs[ZYGOTE_IDX].fd = zygote_socket_fd;
+ fd_structs[ZYGOTE_IDX].events = POLLIN;
+ fd_structs[SESSION_IDX].fd = session_socket;
+ fd_structs[SESSION_IDX].events = POLLIN;
+
+ struct timeval timeout;
+ socklen_t timeout_size = sizeof timeout;
+ if (getsockopt(session_socket, SOL_SOCKET, SO_RCVTIMEO, &timeout, &timeout_size) != 0) {
+ fail_fn_z("Failed to retrieve session socket timeout");
+ }
+
+ struct ucred credentials;
+ socklen_t cred_size = sizeof credentials;
+ if (getsockopt(n_buffer->getFd(), SOL_SOCKET, SO_PEERCRED, &credentials, &cred_size) == -1
+ || cred_size != sizeof credentials) {
+ fail_fn_1("ForkMany failed to get initial credentials, %s", strerror(errno));
+ }
+
+ bool first_time = true;
+ do {
+ if (credentials.uid != expected_uid) {
+ return JNI_FALSE;
+ }
+ n_buffer->readAllLines(first_time ? fail_fn_1 : fail_fn_n);
+ n_buffer->reset();
+ int pid = zygote::forkApp(env, /* no pipe FDs */ -1, -1, session_socket_fds,
+ /*args_known=*/ true, /*is_priority_fork=*/ true,
+ /*purge=*/ first_time);
+ if (pid == 0) {
+ return JNI_TRUE;
+ }
+ // We're in the parent. Write big-endian pid, followed by a boolean.
+ char pid_buf[5];
+ int tmp_pid = pid;
+ for (int i = 3; i >= 0; --i) {
+ pid_buf[i] = tmp_pid & 0xff;
+ tmp_pid >>= 8;
+ }
+ pid_buf[4] = 0; // Process is not wrapped.
+ int res = write(session_socket, pid_buf, 5);
+ if (res != 5) {
+ if (res == -1) {
+ (first_time ? fail_fn_1 : fail_fn_n)
+ (CREATE_ERROR("Pid write error %d: %s", errno, strerror(errno)));
+ } else {
+ (first_time ? fail_fn_1 : fail_fn_n)
+ (CREATE_ERROR("Write unexpectedly returned short: %d < 5", res));
+ }
+ }
+ // Clear buffer and get count from next command.
+ n_buffer->clear();
+ for (;;) {
+ // Poll isn't strictly necessary for now. But without it, disconnect is hard to detect.
+ int poll_res = TEMP_FAILURE_RETRY(poll(fd_structs, 2, -1 /* infinite timeout */));
+ if ((fd_structs[SESSION_IDX].revents & POLLIN) != 0) {
+ if (n_buffer->getCount(fail_fn_z) != 0) {
+ break;
+ } // else disconnected;
+ } else if (poll_res == 0 || (fd_structs[ZYGOTE_IDX].revents & POLLIN) == 0) {
+ fail_fn_z(
+ CREATE_ERROR("Poll returned with no descriptors ready! Poll returned %d", poll_res));
+ }
+ // We've now seen either a disconnect or connect request.
+ close(session_socket);
+ int new_fd = accept(zygote_socket_fd, nullptr, nullptr);
+ if (new_fd == -1) {
+ fail_fn_z("Accept(%d) failed: %s", zygote_socket_fd, strerror(errno));
+ }
+ if (new_fd != session_socket) {
+ // Move new_fd back to the old value, so that we don't have to change Java-level data
+ // structures to reflect a change. This implicitly closes the old one.
+ if (dup2(new_fd, session_socket) != session_socket) {
+ fail_fn_z(CREATE_ERROR("Failed to move fd %d to %d: %s",
+ new_fd, session_socket, strerror(errno)));
+ }
+ close(new_fd);
+ }
+ // If we ever return, we effectively reuse the old Java ZygoteConnection.
+ // None of its state needs to change.
+ if (setsockopt(session_socket, SOL_SOCKET, SO_RCVTIMEO, &timeout, timeout_size) != 0) {
+ fail_fn_z(CREATE_ERROR("Failed to set receive timeout for socket %d: %s",
+ session_socket, strerror(errno)));
+ }
+ if (setsockopt(session_socket, SOL_SOCKET, SO_SNDTIMEO, &timeout, timeout_size) != 0) {
+ fail_fn_z(CREATE_ERROR("Failed to set send timeout for socket %d: %s",
+ session_socket, strerror(errno)));
+ }
+ if (getsockopt(session_socket, SOL_SOCKET, SO_PEERCRED, &credentials, &cred_size) == -1) {
+ fail_fn_z(CREATE_ERROR("ForkMany failed to get credentials: %s", strerror(errno)));
+ }
+ if (cred_size != sizeof credentials) {
+ fail_fn_z(CREATE_ERROR("ForkMany credential size = %d, should be %d",
+ cred_size, static_cast<int>(sizeof credentials)));
+ }
+ }
+ first_time = false;
+ } while (n_buffer->isSimpleForkCommand(minUid, fail_fn_n));
+ ALOGW("forkRepeatedly terminated due to non-simple command");
+ n_buffer->logState();
+ n_buffer->reset();
+ return JNI_FALSE;
+}
+
+#define METHOD_NAME(m) com_android_internal_os_ZygoteCommandBuffer_ ## m
+
+static const JNINativeMethod gMethods[] = {
+ {"getNativeBuffer", "(I)J", (void *) METHOD_NAME(getNativeBuffer)},
+ {"freeNativeBuffer", "(J)V", (void *) METHOD_NAME(freeNativeBuffer)},
+ {"insert", "(JLjava/lang/String;)V", (void *) METHOD_NAME(insert)},
+ {"nativeNextArg", "(J)Ljava/lang/String;", (void *) METHOD_NAME(nativeNextArg)},
+ {"nativeReadFullyAndReset", "(J)V", (void *) METHOD_NAME(nativeReadFullyAndReset)},
+ {"nativeGetCount", "(J)I", (void *) METHOD_NAME(nativeGetCount)},
+ {"nativeForkRepeatedly", "(JIIILjava/lang/String;)Z",
+ (void *) METHOD_NAME(nativeForkRepeatedly)},
+};
+
+int register_com_android_internal_os_ZygoteCommandBuffer(JNIEnv* env) {
+ return RegisterMethodsOrDie(env, "com/android/internal/os/ZygoteCommandBuffer", gMethods,
+ NELEM(gMethods));
+}
+
+} // namespace android
diff --git a/core/proto/android/service/netstats.proto b/core/proto/android/service/netstats.proto
index 8ebb4a9f6649..c8cdfddc3985 100644
--- a/core/proto/android/service/netstats.proto
+++ b/core/proto/android/service/netstats.proto
@@ -80,6 +80,8 @@ message NetworkIdentityProto {
optional bool metered = 5;
optional bool default_network = 6;
+
+ optional int32 oem_managed_network = 7;
}
// Corresponds to NetworkStatsRecorder.
diff --git a/keystore/java/android/security/AndroidKeyStoreMaintenance.java b/keystore/java/android/security/AndroidKeyStoreMaintenance.java
index c81c8c54d88a..ed789f03f9ba 100644
--- a/keystore/java/android/security/AndroidKeyStoreMaintenance.java
+++ b/keystore/java/android/security/AndroidKeyStoreMaintenance.java
@@ -21,6 +21,7 @@ import android.annotation.Nullable;
import android.os.ServiceManager;
import android.os.ServiceSpecificException;
import android.security.usermanager.IKeystoreUserManager;
+import android.system.keystore2.Domain;
import android.system.keystore2.ResponseCode;
import android.util.Log;
@@ -39,7 +40,7 @@ public class AndroidKeyStoreMaintenance {
}
/**
- * Informs keystore2 about adding a user
+ * Informs Keystore 2.0 about adding a user
*
* @param userId - Android user id of the user being added
* @return 0 if successful or a {@code ResponseCode}
@@ -60,7 +61,7 @@ public class AndroidKeyStoreMaintenance {
}
/**
- * Informs keystore2 about removing a usergit mer
+ * Informs Keystore 2.0 about removing a usergit mer
*
* @param userId - Android user id of the user being removed
* @return 0 if successful or a {@code ResponseCode}
@@ -81,7 +82,7 @@ public class AndroidKeyStoreMaintenance {
}
/**
- * Informs keystore2 about changing user's password
+ * Informs Keystore 2.0 about changing user's password
*
* @param userId - Android user id of the user
* @param password - a secret derived from the synthetic password provided by the
@@ -102,4 +103,22 @@ public class AndroidKeyStoreMaintenance {
return SYSTEM_ERROR;
}
}
+
+ /**
+ * Informs Keystore 2.0 that an app was uninstalled and the corresponding namspace is to
+ * be cleared.
+ */
+ public static int clearNamespace(@Domain int domain, long namespace) {
+ if (!android.security.keystore2.AndroidKeyStoreProvider.isInstalled()) return 0;
+ try {
+ getService().clearNamespace(domain, namespace);
+ return 0;
+ } catch (ServiceSpecificException e) {
+ Log.e(TAG, "clearNamespace failed", e);
+ return e.errorCode;
+ } catch (Exception e) {
+ Log.e(TAG, "Can not connect to keystore", e);
+ return SYSTEM_ERROR;
+ }
+ }
}
diff --git a/keystore/java/android/security/KeyStore.java b/keystore/java/android/security/KeyStore.java
index 198df40c7d7b..93658e69eac8 100644
--- a/keystore/java/android/security/KeyStore.java
+++ b/keystore/java/android/security/KeyStore.java
@@ -43,6 +43,7 @@ import android.security.keystore.KeyPermanentlyInvalidatedException;
import android.security.keystore.KeyProperties;
import android.security.keystore.KeystoreResponse;
import android.security.keystore.UserNotAuthenticatedException;
+import android.system.keystore2.Domain;
import android.util.Log;
import com.android.internal.org.bouncycastle.asn1.ASN1InputStream;
@@ -466,6 +467,9 @@ public class KeyStore {
public boolean clearUid(int uid) {
try {
+ if (android.security.keystore2.AndroidKeyStoreProvider.isInstalled()) {
+ return AndroidKeyStoreMaintenance.clearNamespace(Domain.APP, uid) == 0;
+ }
return mBinder.clear_uid(uid) == NO_ERROR;
} catch (RemoteException e) {
Log.w(TAG, "Cannot connect to keystore", e);
diff --git a/keystore/java/android/security/KeyStore2.java b/keystore/java/android/security/KeyStore2.java
index 476e4d7b7b18..6ac3821d0f9c 100644
--- a/keystore/java/android/security/KeyStore2.java
+++ b/keystore/java/android/security/KeyStore2.java
@@ -24,6 +24,7 @@ import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.ServiceSpecificException;
import android.security.keymaster.KeymasterDefs;
+import android.system.keystore2.Domain;
import android.system.keystore2.IKeystoreService;
import android.system.keystore2.KeyDescriptor;
import android.system.keystore2.KeyEntryResponse;
@@ -157,6 +158,50 @@ public class KeyStore2 {
}
/**
+ * Grant string prefix as used by the keystore boringssl engine. Must be kept in sync
+ * with system/security/keystore-engine. Note: The prefix here includes the 0x which
+ * std::stringstream used in keystore-engine needs to identify the number as hex represented.
+ * Here we include it in the prefix, because Long#parseUnsignedLong does not understand it
+ * and gets the radix as explicit argument.
+ * @hide
+ */
+ private static final String KEYSTORE_ENGINE_GRANT_ALIAS_PREFIX =
+ "ks2_keystore-engine_grant_id:0x";
+
+ /**
+ * This function turns a grant identifier into a specific string that is understood by the
+ * keystore-engine in system/security/keystore-engine. Is only used by VPN and WI-FI components
+ * to allow certain system components like racoon or vendor components like WPA supplicant
+ * to use keystore keys with boring ssl.
+ *
+ * @param grantId the grant id as returned by {@link #grant} in the {@code nspace} filed of
+ * the resulting {@code KeyDescriptor}.
+ * @return The grant descriptor string.
+ * @hide
+ */
+ public static String makeKeystoreEngineGrantString(long grantId) {
+ return String.format("%s%016X", KEYSTORE_ENGINE_GRANT_ALIAS_PREFIX, grantId);
+ }
+
+ /**
+ * Convenience function to turn a keystore engine grant string as returned by
+ * {@link #makeKeystoreEngineGrantString(long)} back into a grant KeyDescriptor.
+ *
+ * @param grantString As string returned by {@link #makeKeystoreEngineGrantString(long)}
+ * @return The grant key descriptor.
+ * @hide
+ */
+ public static KeyDescriptor keystoreEngineGrantString2KeyDescriptor(String grantString) {
+ KeyDescriptor key = new KeyDescriptor();
+ key.domain = Domain.GRANT;
+ key.nspace = Long.parseUnsignedLong(
+ grantString.substring(KEYSTORE_ENGINE_GRANT_ALIAS_PREFIX.length()), 16);
+ key.alias = null;
+ key.blob = null;
+ return key;
+ }
+
+ /**
* Create a grant that allows the grantee identified by {@code granteeUid} to use
* the key specified by {@code descriptor} withint the restrictions given by
* {@code accessVectore}.
diff --git a/libs/hwui/RecordingCanvas.cpp b/libs/hwui/RecordingCanvas.cpp
index dc467c41baed..41ecd5e49acd 100644
--- a/libs/hwui/RecordingCanvas.cpp
+++ b/libs/hwui/RecordingCanvas.cpp
@@ -401,10 +401,11 @@ struct DrawImageLattice final : Op {
struct DrawTextBlob final : Op {
static const auto kType = Type::DrawTextBlob;
DrawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y, const SkPaint& paint)
- : blob(sk_ref_sp(blob)), x(x), y(y), paint(paint) {}
+ : blob(sk_ref_sp(blob)), x(x), y(y), paint(paint), drawTextBlobMode(gDrawTextBlobMode) {}
sk_sp<const SkTextBlob> blob;
SkScalar x, y;
SkPaint paint;
+ DrawTextBlobMode drawTextBlobMode;
void draw(SkCanvas* c, const SkMatrix&) const { c->drawTextBlob(blob.get(), x, y, paint); }
};
@@ -791,6 +792,24 @@ constexpr color_transform_fn colorTransformForOp() {
}
}
+template<>
+constexpr color_transform_fn colorTransformForOp<DrawTextBlob>() {
+ return [](const void *opRaw, ColorTransform transform) {
+ const DrawTextBlob *op = reinterpret_cast<const DrawTextBlob*>(opRaw);
+ switch (op->drawTextBlobMode) {
+ case DrawTextBlobMode::HctOutline:
+ const_cast<SkPaint&>(op->paint).setColor(SK_ColorBLACK);
+ break;
+ case DrawTextBlobMode::HctInner:
+ const_cast<SkPaint&>(op->paint).setColor(SK_ColorWHITE);
+ break;
+ default:
+ transformPaint(transform, const_cast<SkPaint*>(&(op->paint)));
+ break;
+ }
+ };
+}
+
#define X(T) colorTransformForOp<T>(),
static const color_transform_fn color_transform_fns[] = {
#include "DisplayListOps.in"
diff --git a/libs/hwui/hwui/Canvas.cpp b/libs/hwui/hwui/Canvas.cpp
index c138a32eacc2..14906d5c1166 100644
--- a/libs/hwui/hwui/Canvas.cpp
+++ b/libs/hwui/hwui/Canvas.cpp
@@ -111,6 +111,7 @@ public:
bool darken = channelSum < (128 * 3);
// outline
+ gDrawTextBlobMode = DrawTextBlobMode::HctOutline;
Paint outlinePaint(paint);
simplifyPaint(darken ? SK_ColorWHITE : SK_ColorBLACK, &outlinePaint);
outlinePaint.setStyle(SkPaint::kStrokeAndFill_Style);
@@ -118,11 +119,14 @@ public:
bounds.mRight, bounds.mBottom, totalAdvance);
// inner
+ gDrawTextBlobMode = DrawTextBlobMode::HctInner;
Paint innerPaint(paint);
simplifyPaint(darken ? SK_ColorBLACK : SK_ColorWHITE, &innerPaint);
innerPaint.setStyle(SkPaint::kFill_Style);
canvas->drawGlyphs(glyphFunc, glyphCount, innerPaint, x, y, bounds.mLeft, bounds.mTop,
bounds.mRight, bounds.mBottom, totalAdvance);
+
+ gDrawTextBlobMode = DrawTextBlobMode::Normal;
} else {
// standard draw path
canvas->drawGlyphs(glyphFunc, glyphCount, paint, x, y, bounds.mLeft, bounds.mTop,
diff --git a/libs/hwui/hwui/Canvas.h b/libs/hwui/hwui/Canvas.h
index 27dfed305a94..d9928059ed30 100644
--- a/libs/hwui/hwui/Canvas.h
+++ b/libs/hwui/hwui/Canvas.h
@@ -87,6 +87,14 @@ class Bitmap;
class Paint;
struct Typeface;
+enum class DrawTextBlobMode {
+ Normal,
+ HctOutline,
+ HctInner,
+};
+
+inline DrawTextBlobMode gDrawTextBlobMode = DrawTextBlobMode::Normal;
+
class ANDROID_API Canvas {
public:
virtual ~Canvas(){};
diff --git a/media/java/android/media/soundtrigger/OWNERS b/media/java/android/media/soundtrigger/OWNERS
index 6a351d396836..e5d037003ac4 100644
--- a/media/java/android/media/soundtrigger/OWNERS
+++ b/media/java/android/media/soundtrigger/OWNERS
@@ -1 +1,2 @@
+ytai@google.com
elaurent@google.com
diff --git a/packages/Connectivity/framework/src/android/net/NetworkRequest.java b/packages/Connectivity/framework/src/android/net/NetworkRequest.java
index 4e3085f4704d..b4a651c0607e 100644
--- a/packages/Connectivity/framework/src/android/net/NetworkRequest.java
+++ b/packages/Connectivity/framework/src/android/net/NetworkRequest.java
@@ -16,6 +16,22 @@
package android.net;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_CAPTIVE_PORTAL;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_DUN;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_FOREGROUND;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_CONGESTED;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VPN;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_PARTIAL_CONNECTIVITY;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_TEMPORARILY_NOT_METERED;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_TRUSTED;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
@@ -30,6 +46,8 @@ import android.os.Process;
import android.text.TextUtils;
import android.util.proto.ProtoOutputStream;
+import java.util.Arrays;
+import java.util.List;
import java.util.Objects;
import java.util.Set;
@@ -154,8 +172,30 @@ public class NetworkRequest implements Parcelable {
* needed in terms of {@link NetworkCapabilities} features
*/
public static class Builder {
+ /**
+ * Capabilities that are currently compatible with VCN networks.
+ */
+ private static final List<Integer> VCN_SUPPORTED_CAPABILITIES = Arrays.asList(
+ NET_CAPABILITY_CAPTIVE_PORTAL,
+ NET_CAPABILITY_DUN,
+ NET_CAPABILITY_FOREGROUND,
+ NET_CAPABILITY_INTERNET,
+ NET_CAPABILITY_NOT_CONGESTED,
+ NET_CAPABILITY_NOT_METERED,
+ NET_CAPABILITY_NOT_RESTRICTED,
+ NET_CAPABILITY_NOT_ROAMING,
+ NET_CAPABILITY_NOT_SUSPENDED,
+ NET_CAPABILITY_NOT_VPN,
+ NET_CAPABILITY_PARTIAL_CONNECTIVITY,
+ NET_CAPABILITY_TEMPORARILY_NOT_METERED,
+ NET_CAPABILITY_TRUSTED,
+ NET_CAPABILITY_VALIDATED);
+
private final NetworkCapabilities mNetworkCapabilities;
+ // A boolean that represents the user modified NOT_VCN_MANAGED capability.
+ private boolean mModifiedNotVcnManaged = false;
+
/**
* Default constructor for Builder.
*/
@@ -177,6 +217,7 @@ public class NetworkRequest implements Parcelable {
// maybeMarkCapabilitiesRestricted() doesn't add back.
final NetworkCapabilities nc = new NetworkCapabilities(mNetworkCapabilities);
nc.maybeMarkCapabilitiesRestricted();
+ deduceNotVcnManagedCapability(nc);
return new NetworkRequest(nc, ConnectivityManager.TYPE_NONE,
ConnectivityManager.REQUEST_ID_UNSET, Type.NONE);
}
@@ -193,6 +234,9 @@ 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;
+ }
return this;
}
@@ -204,6 +248,9 @@ 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;
+ }
return this;
}
@@ -261,6 +308,9 @@ public class NetworkRequest implements Parcelable {
@NonNull
public Builder clearCapabilities() {
mNetworkCapabilities.clearAll();
+ // If the caller explicitly clear all capabilities, the NOT_VCN_MANAGED capabilities
+ // should not be add back later.
+ mModifiedNotVcnManaged = true;
return this;
}
@@ -380,6 +430,25 @@ public class NetworkRequest implements Parcelable {
mNetworkCapabilities.setSignalStrength(signalStrength);
return this;
}
+
+ /**
+ * Deduce the NET_CAPABILITY_NOT_VCN_MANAGED capability from other capabilities
+ * and user intention, which includes:
+ * 1. For the requests that don't have anything besides
+ * {@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,
+ * do not alter them to allow user fire request that suits their need.
+ *
+ * @hide
+ */
+ private void deduceNotVcnManagedCapability(final NetworkCapabilities nc) {
+ if (mModifiedNotVcnManaged) return;
+ for (final int cap : nc.getCapabilities()) {
+ if (!VCN_SUPPORTED_CAPABILITIES.contains(cap)) return;
+ }
+ nc.addCapability(NET_CAPABILITY_NOT_VCN_MANAGED);
+ }
}
// implement the Parcelable interface
diff --git a/packages/Connectivity/framework/src/android/net/NetworkUtils.java b/packages/Connectivity/framework/src/android/net/NetworkUtils.java
index 9ccb04a44af4..b5e8a614b8ea 100644
--- a/packages/Connectivity/framework/src/android/net/NetworkUtils.java
+++ b/packages/Connectivity/framework/src/android/net/NetworkUtils.java
@@ -91,7 +91,8 @@ public class NetworkUtils {
* this socket will go directly to the underlying network, so its traffic will not be
* forwarded through the VPN.
*/
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553,
+ publicAlternatives = "Use {@link android.net.VpnService#protect} instead.")
public static native boolean protectFromVpn(FileDescriptor fd);
/**
diff --git a/packages/Connectivity/framework/src/android/net/VpnTransportInfo.java b/packages/Connectivity/framework/src/android/net/VpnTransportInfo.java
index 0242ba08742c..340141b78aa5 100644
--- a/packages/Connectivity/framework/src/android/net/VpnTransportInfo.java
+++ b/packages/Connectivity/framework/src/android/net/VpnTransportInfo.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2017 The Android Open Source Project
+ * 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.
diff --git a/packages/Connectivity/framework/src/android/net/util/MultinetworkPolicyTracker.java b/packages/Connectivity/framework/src/android/net/util/MultinetworkPolicyTracker.java
index 85e3fa3048ed..43fffd733e91 100644
--- a/packages/Connectivity/framework/src/android/net/util/MultinetworkPolicyTracker.java
+++ b/packages/Connectivity/framework/src/android/net/util/MultinetworkPolicyTracker.java
@@ -40,6 +40,8 @@ import com.android.internal.annotations.VisibleForTesting;
import java.util.Arrays;
import java.util.List;
+import java.util.concurrent.Executor;
+import java.util.concurrent.RejectedExecutionException;
/**
* A class to encapsulate management of the "Smart Networking" capability of
@@ -73,6 +75,32 @@ public class MultinetworkPolicyTracker {
private volatile int mMeteredMultipathPreference;
private int mActiveSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
+ // Mainline module can't use internal HandlerExecutor, so add an identical executor here.
+ private static class HandlerExecutor implements Executor {
+ @NonNull
+ private final Handler mHandler;
+
+ HandlerExecutor(@NonNull Handler handler) {
+ mHandler = handler;
+ }
+ @Override
+ public void execute(Runnable command) {
+ if (!mHandler.post(command)) {
+ throw new RejectedExecutionException(mHandler + " is shutting down");
+ }
+ }
+ }
+
+ @VisibleForTesting
+ protected class ActiveDataSubscriptionIdChangedListener extends PhoneStateListener
+ implements PhoneStateListener.ActiveDataSubscriptionIdChangedListener {
+ @Override
+ public void onActiveDataSubscriptionIdChanged(int subId) {
+ mActiveSubId = subId;
+ reevaluateInternal();
+ }
+ }
+
public MultinetworkPolicyTracker(Context ctx, Handler handler) {
this(ctx, handler, null);
}
@@ -93,14 +121,8 @@ public class MultinetworkPolicyTracker {
}
};
- ctx.getSystemService(TelephonyManager.class).listen(
- new PhoneStateListener(handler.getLooper()) {
- @Override
- public void onActiveDataSubscriptionIdChanged(int subId) {
- mActiveSubId = subId;
- reevaluateInternal();
- }
- }, PhoneStateListener.LISTEN_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGE);
+ ctx.getSystemService(TelephonyManager.class).registerPhoneStateListener(
+ new HandlerExecutor(handler), new ActiveDataSubscriptionIdChangedListener());
updateAvoidBadWifi();
updateMeteredMultipathPreference();
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index f4138d10a84d..542d527177a1 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -44,6 +44,7 @@ import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED;
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;
@@ -91,7 +92,6 @@ import android.net.IConnectivityManager;
import android.net.IDnsResolver;
import android.net.INetd;
import android.net.INetworkActivityListener;
-import android.net.INetworkManagementEventObserver;
import android.net.INetworkMonitor;
import android.net.INetworkMonitorCallbacks;
import android.net.INetworkPolicyListener;
@@ -194,6 +194,7 @@ import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.LocationPermissionChecker;
import com.android.internal.util.MessageUtils;
import com.android.modules.utils.BasicShellCommandHandler;
+import com.android.net.module.util.BaseNetdUnsolicitedEventListener;
import com.android.net.module.util.CollectionUtils;
import com.android.net.module.util.LinkPropertiesUtils.CompareOrUpdateResult;
import com.android.net.module.util.LinkPropertiesUtils.CompareResult;
@@ -214,7 +215,6 @@ import com.android.server.connectivity.NetworkRanker;
import com.android.server.connectivity.PermissionMonitor;
import com.android.server.connectivity.ProxyTracker;
import com.android.server.connectivity.QosCallbackTracker;
-import com.android.server.net.BaseNetworkObserver;
import com.android.server.net.NetworkPolicyManagerInternal;
import com.android.server.utils.PriorityDump;
@@ -332,6 +332,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
private INetworkStatsService mStatsService;
private NetworkPolicyManager mPolicyManager;
private NetworkPolicyManagerInternal mPolicyManagerInternal;
+ private final NetdCallback mNetdCallback;
/**
* TestNetworkService (lazily) created upon first usage. Locked to prevent creation of multiple
@@ -1204,6 +1205,13 @@ public class ConnectivityService extends IConnectivityManager.Stub
mNetworkActivityTracker = new LegacyNetworkActivityTracker(mContext, mHandler, mNMS, mNetd);
+ mNetdCallback = new NetdCallback();
+ try {
+ mNetd.registerUnsolicitedEventListener(mNetdCallback);
+ } catch (RemoteException | ServiceSpecificException e) {
+ loge("Error registering event listener :" + e);
+ }
+
mSettingsObserver = new SettingsObserver(mContext, mHandler);
registerSettingsCallbacks();
@@ -1241,6 +1249,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
private static NetworkCapabilities createDefaultNetworkCapabilitiesForUid(int uid) {
final NetworkCapabilities netCap = new NetworkCapabilities();
netCap.addCapability(NET_CAPABILITY_INTERNET);
+ netCap.addCapability(NET_CAPABILITY_NOT_VCN_MANAGED);
netCap.removeCapability(NET_CAPABILITY_NOT_VPN);
netCap.setSingleUid(uid);
return netCap;
@@ -1255,6 +1264,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
int transportType, NetworkRequest.Type type) {
final NetworkCapabilities netCap = new NetworkCapabilities();
netCap.addCapability(NET_CAPABILITY_INTERNET);
+ netCap.addCapability(NET_CAPABILITY_NOT_VCN_MANAGED);
netCap.setRequestorUidAndPackageName(Process.myUid(), mContext.getPackageName());
if (transportType > TYPE_NONE) {
netCap.addTransportType(transportType);
@@ -8649,6 +8659,14 @@ public class ConnectivityService extends IConnectivityManager.Stub
notifyDataStallSuspected(p, network.getNetId());
}
+ private class NetdCallback extends BaseNetdUnsolicitedEventListener {
+ @Override
+ public void onInterfaceClassActivityChanged(boolean isActive, int timerLabel,
+ long timestampNs, int uid) {
+ mNetworkActivityTracker.setAndReportNetworkActive(isActive, timerLabel, timestampNs);
+ }
+ }
+
private final LegacyNetworkActivityTracker mNetworkActivityTracker;
/**
@@ -8659,7 +8677,6 @@ public class ConnectivityService extends IConnectivityManager.Stub
private static final int NO_UID = -1;
private final Context mContext;
private final INetd mNetd;
- private final INetworkManagementService mNMS;
private final RemoteCallbackList<INetworkActivityListener> mNetworkActivityListeners =
new RemoteCallbackList<>();
// Indicate the current system default network activity is active or not.
@@ -8682,41 +8699,27 @@ public class ConnectivityService extends IConnectivityManager.Stub
LegacyNetworkActivityTracker(@NonNull Context context, @NonNull Handler handler,
@NonNull INetworkManagementService nms, @NonNull INetd netd) {
mContext = context;
- mNMS = nms;
mNetd = netd;
mHandler = handler;
- try {
- mNMS.registerObserver(mDataActivityObserver);
- } catch (RemoteException e) {
- loge("Error registering observer :" + e);
- }
- }
-
- // TODO: Migrate away the dependency with INetworkManagementEventObserver.
- private final INetworkManagementEventObserver mDataActivityObserver =
- new BaseNetworkObserver() {
- @Override
- public void interfaceClassDataActivityChanged(int transportType, boolean active,
- long tsNanos, int uid) {
- sendDataActivityBroadcast(transportTypeToLegacyType(transportType), active,
- tsNanos);
- synchronized (mActiveIdleTimers) {
- mNetworkActive = active;
- // If there are no idle timers, it means that system is not monitoring
- // activity, so the system default network for those default network
- // unspecified apps is always considered active.
- //
- // TODO: If the mActiveIdleTimers is empty, netd will actually not send
- // any network activity change event. Whenever this event is received,
- // the mActiveIdleTimers should be always not empty. The legacy behavior
- // is no-op. Remove to refer to mNetworkActive only.
- if (mNetworkActive || mActiveIdleTimers.isEmpty()) {
- mHandler.sendMessage(
- mHandler.obtainMessage(EVENT_REPORT_NETWORK_ACTIVITY));
- }
- }
- }
- };
+ }
+
+ public void setAndReportNetworkActive(boolean active, int transportType, long tsNanos) {
+ sendDataActivityBroadcast(transportTypeToLegacyType(transportType), active, tsNanos);
+ synchronized (mActiveIdleTimers) {
+ mNetworkActive = active;
+ // If there are no idle timers, it means that system is not monitoring
+ // activity, so the system default network for those default network
+ // unspecified apps is always considered active.
+ //
+ // TODO: If the mActiveIdleTimers is empty, netd will actually not send
+ // any network activity change event. Whenever this event is received,
+ // the mActiveIdleTimers should be always not empty. The legacy behavior
+ // is no-op. Remove to refer to mNetworkActive only.
+ if (mNetworkActive || mActiveIdleTimers.isEmpty()) {
+ mHandler.sendMessage(mHandler.obtainMessage(EVENT_REPORT_NETWORK_ACTIVITY));
+ }
+ }
+ }
// The network activity should only be updated from ConnectivityService handler thread
// when mActiveIdleTimers lock is held.
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index d0b3047a7fe9..a8707ee0ffe8 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -183,6 +183,7 @@ import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.ProcessMemoryState;
import android.app.ProfilerInfo;
+import android.app.PropertyInvalidatedCache;
import android.app.WaitResult;
import android.app.backup.IBackupManager;
import android.app.usage.UsageEvents;
@@ -12844,6 +12845,10 @@ public class ActivityManagerService extends IActivityManager.Stub
if (r.thread != null) {
pw.println("\n\n** Cache info for pid " + r.pid + " [" + r.processName + "] **");
pw.flush();
+ if (r.pid == MY_PID) {
+ PropertyInvalidatedCache.dumpCacheInfo(fd, args);
+ continue;
+ }
try {
TransferPipe tp = new TransferPipe();
try {
diff --git a/services/core/java/com/android/server/connectivity/MultipathPolicyTracker.java b/services/core/java/com/android/server/connectivity/MultipathPolicyTracker.java
index 21ef356c962c..4ecc7594a79c 100644
--- a/services/core/java/com/android/server/connectivity/MultipathPolicyTracker.java
+++ b/services/core/java/com/android/server/connectivity/MultipathPolicyTracker.java
@@ -26,6 +26,7 @@ import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
import static android.net.NetworkPolicy.LIMIT_DISABLED;
import static android.net.NetworkPolicy.WARNING_DISABLED;
import static android.net.NetworkTemplate.NETWORK_TYPE_ALL;
+import static android.net.NetworkTemplate.OEM_MANAGED_ALL;
import static android.provider.Settings.Global.NETWORK_DEFAULT_DAILY_MULTIPATH_QUOTA_BYTES;
import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
@@ -226,7 +227,7 @@ public class MultipathPolicyTracker {
mNetworkTemplate = new NetworkTemplate(
NetworkTemplate.MATCH_MOBILE, subscriberId, new String[] { subscriberId },
null, NetworkStats.METERED_ALL, NetworkStats.ROAMING_ALL,
- NetworkStats.DEFAULT_NETWORK_NO, NETWORK_TYPE_ALL);
+ NetworkStats.DEFAULT_NETWORK_NO, NETWORK_TYPE_ALL, OEM_MANAGED_ALL);
mUsageCallback = new UsageCallback() {
@Override
public void onThresholdReached(int networkType, String subscriberId) {
@@ -274,7 +275,8 @@ public class MultipathPolicyTracker {
null /* networkId, unused for matching mobile networks */,
!nc.hasCapability(NET_CAPABILITY_NOT_ROAMING),
!nc.hasCapability(NET_CAPABILITY_NOT_METERED),
- false /* defaultNetwork, templates should have DEFAULT_NETWORK_ALL */);
+ false /* defaultNetwork, templates should have DEFAULT_NETWORK_ALL */,
+ OEM_MANAGED_ALL);
}
private long getRemainingDailyBudget(long limitBytes,
diff --git a/services/core/java/com/android/server/locksettings/OWNERS b/services/core/java/com/android/server/locksettings/OWNERS
index 08b8a8c106b7..7577ee5c9fde 100644
--- a/services/core/java/com/android/server/locksettings/OWNERS
+++ b/services/core/java/com/android/server/locksettings/OWNERS
@@ -1,3 +1,4 @@
jaggies@google.com
kchyn@google.com
rubinxu@google.com
+xunchang@google.com
diff --git a/services/core/java/com/android/server/locksettings/RebootEscrowProviderServerBasedImpl.java b/services/core/java/com/android/server/locksettings/RebootEscrowProviderServerBasedImpl.java
index b3b45460899d..697bf08a232e 100644
--- a/services/core/java/com/android/server/locksettings/RebootEscrowProviderServerBasedImpl.java
+++ b/services/core/java/com/android/server/locksettings/RebootEscrowProviderServerBasedImpl.java
@@ -44,7 +44,7 @@ class RebootEscrowProviderServerBasedImpl implements RebootEscrowProviderInterfa
* Use the default lifetime of 10 minutes. The lifetime covers the following activities:
* Server wrap secret -> device reboot -> server unwrap blob.
*/
- private static final long DEFAULT_SERVER_BLOB_LIFETIME_IN_MILLIS = 600_1000;
+ private static final long DEFAULT_SERVER_BLOB_LIFETIME_IN_MILLIS = 600_000;
private final LockSettingsStorage mStorage;
diff --git a/services/core/java/com/android/server/net/NetworkIdentitySet.java b/services/core/java/com/android/server/net/NetworkIdentitySet.java
index bce80696f72c..22ed781da92d 100644
--- a/services/core/java/com/android/server/net/NetworkIdentitySet.java
+++ b/services/core/java/com/android/server/net/NetworkIdentitySet.java
@@ -40,6 +40,7 @@ public class NetworkIdentitySet extends HashSet<NetworkIdentity> implements
private static final int VERSION_ADD_NETWORK_ID = 3;
private static final int VERSION_ADD_METERED = 4;
private static final int VERSION_ADD_DEFAULT_NETWORK = 5;
+ private static final int VERSION_ADD_OEM_MANAGED_NETWORK = 6;
public NetworkIdentitySet() {
}
@@ -84,13 +85,20 @@ public class NetworkIdentitySet extends HashSet<NetworkIdentity> implements
defaultNetwork = true;
}
+ final int oemNetCapabilities;
+ if (version >= VERSION_ADD_OEM_MANAGED_NETWORK) {
+ oemNetCapabilities = in.readInt();
+ } else {
+ oemNetCapabilities = NetworkIdentity.OEM_NONE;
+ }
+
add(new NetworkIdentity(type, subType, subscriberId, networkId, roaming, metered,
- defaultNetwork));
+ defaultNetwork, oemNetCapabilities));
}
}
public void writeToStream(DataOutput out) throws IOException {
- out.writeInt(VERSION_ADD_DEFAULT_NETWORK);
+ out.writeInt(VERSION_ADD_OEM_MANAGED_NETWORK);
out.writeInt(size());
for (NetworkIdentity ident : this) {
out.writeInt(ident.getType());
@@ -100,6 +108,7 @@ public class NetworkIdentitySet extends HashSet<NetworkIdentity> implements
out.writeBoolean(ident.getRoaming());
out.writeBoolean(ident.getMetered());
out.writeBoolean(ident.getDefaultNetwork());
+ out.writeInt(ident.getOemManaged());
}
}
diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index b5c0f28d8ba2..46ad4573512e 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -52,6 +52,7 @@ import static android.net.INetd.FIREWALL_RULE_DENY;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING;
import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
+import static android.net.NetworkIdentity.OEM_NONE;
import static android.net.NetworkPolicy.LIMIT_DISABLED;
import static android.net.NetworkPolicy.SNOOZE_NEVER;
import static android.net.NetworkPolicy.WARNING_DISABLED;
@@ -1383,7 +1384,11 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
final String subscriberId = mSubIdToSubscriberId.valueAt(i);
final NetworkIdentity probeIdent = new NetworkIdentity(TYPE_MOBILE,
TelephonyManager.NETWORK_TYPE_UNKNOWN, subscriberId, null, false, true,
- true);
+ true, OEM_NONE);
+ /* While OEM_NONE indicates "any non OEM managed network", OEM_NONE is meant to be a
+ * placeholder value here. The probeIdent is matched against a NetworkTemplate which
+ * should have its OEM managed value set to OEM_MANAGED_ALL, which will cause the
+ * template to match probeIdent without regard to OEM managed status. */
if (template.matches(probeIdent)) {
return subId;
}
@@ -1613,7 +1618,8 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
// find and update the mobile NetworkPolicy for this subscriber id
boolean policyUpdated = false;
final NetworkIdentity probeIdent = new NetworkIdentity(TYPE_MOBILE,
- TelephonyManager.NETWORK_TYPE_UNKNOWN, subscriberId, null, false, true, true);
+ TelephonyManager.NETWORK_TYPE_UNKNOWN, subscriberId, null, false, true, true,
+ OEM_NONE);
for (int i = mNetworkPolicy.size() - 1; i >= 0; i--) {
final NetworkTemplate template = mNetworkPolicy.keyAt(i);
if (template.matches(probeIdent)) {
@@ -1842,7 +1848,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
final NetworkIdentity probeIdent = new NetworkIdentity(TYPE_MOBILE,
TelephonyManager.NETWORK_TYPE_UNKNOWN, subscriberId, null, false, true,
- true);
+ true, OEM_NONE);
// Template is matched when subscriber id matches.
if (template.matches(probeIdent)) {
matchingSubIds.add(subId);
@@ -2157,7 +2163,8 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
private boolean ensureActiveMobilePolicyAL(int subId, String subscriberId) {
// Poke around to see if we already have a policy
final NetworkIdentity probeIdent = new NetworkIdentity(TYPE_MOBILE,
- TelephonyManager.NETWORK_TYPE_UNKNOWN, subscriberId, null, false, true, true);
+ TelephonyManager.NETWORK_TYPE_UNKNOWN, subscriberId, null, false, true, true,
+ OEM_NONE);
for (int i = mNetworkPolicy.size() - 1; i >= 0; i--) {
final NetworkTemplate template = mNetworkPolicy.keyAt(i);
if (template.matches(probeIdent)) {
diff --git a/services/core/java/com/android/server/net/NetworkStatsService.java b/services/core/java/com/android/server/net/NetworkStatsService.java
index 9706bcece924..5b9a11bc5a31 100644
--- a/services/core/java/com/android/server/net/NetworkStatsService.java
+++ b/services/core/java/com/android/server/net/NetworkStatsService.java
@@ -1319,7 +1319,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub {
NetworkIdentity vtIdent = new NetworkIdentity(ident.getType(),
ident.getSubType(), ident.getSubscriberId(), ident.getNetworkId(),
ident.getRoaming(), true /* metered */,
- true /* onDefaultNetwork */);
+ true /* onDefaultNetwork */, ident.getOemManaged());
findOrCreateNetworkIdentitySet(mActiveIfaces, IFACE_VT).add(vtIdent);
findOrCreateNetworkIdentitySet(mActiveUidIfaces, IFACE_VT).add(vtIdent);
}
diff --git a/services/core/java/com/android/server/pm/PackageDexOptimizer.java b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
index 4a2fb5da5e70..b39a6b4f4658 100644
--- a/services/core/java/com/android/server/pm/PackageDexOptimizer.java
+++ b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
@@ -780,7 +780,8 @@ public class PackageDexOptimizer {
return getOatDir(codePath).getAbsolutePath();
}
- static File getOatDir(File codePath) {
+ /** Returns the oat dir for the given code path */
+ public static File getOatDir(File codePath) {
return new File(codePath, OAT_DIR_NAME);
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index e0ec6bfb7a70..4896fd91fb6c 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -355,6 +355,7 @@ import com.android.server.pm.Installer.InstallerException;
import com.android.server.pm.Settings.DatabaseVersion;
import com.android.server.pm.Settings.VersionInfo;
import com.android.server.pm.dex.ArtManagerService;
+import com.android.server.pm.dex.ArtUtils;
import com.android.server.pm.dex.DexManager;
import com.android.server.pm.dex.DexoptOptions;
import com.android.server.pm.dex.PackageDexUsage;
@@ -20598,7 +20599,7 @@ public class PackageManagerService extends IPackageManager.Stub
}
final Intent intent = getHomeIntent();
final List<ResolveInfo> resolveInfos = queryIntentActivitiesInternal(intent, null,
- PackageManager.GET_META_DATA, userId);
+ MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE, userId);
final ResolveInfo preferredResolveInfo = findPreferredActivityNotLocked(
intent, null, 0, resolveInfos, 0, true, false, false, userId);
final String packageName = preferredResolveInfo != null
@@ -25538,43 +25539,14 @@ public class PackageManagerService extends IPackageManager.Stub
}
}
- private String getOatDir(AndroidPackage pkg, @NonNull PackageSetting pkgSetting) {
- if (!AndroidPackageUtils.canHaveOatDir(pkg,
- pkgSetting.getPkgState().isUpdatedSystemApp())) {
- return null;
- }
- File codePath = new File(pkg.getCodePath());
- if (codePath.isDirectory()) {
- return PackageDexOptimizer.getOatDir(codePath).getAbsolutePath();
- }
- return null;
- }
-
void deleteOatArtifactsOfPackage(String packageName) {
- final String[] instructionSets;
- final List<String> codePaths;
- final String oatDir;
final AndroidPackage pkg;
final PackageSetting pkgSetting;
synchronized (mLock) {
pkg = mPackages.get(packageName);
pkgSetting = mSettings.getPackageLPr(packageName);
}
- instructionSets = getAppDexInstructionSets(
- AndroidPackageUtils.getPrimaryCpuAbi(pkg, pkgSetting),
- AndroidPackageUtils.getSecondaryCpuAbi(pkg, pkgSetting));
- codePaths = AndroidPackageUtils.getAllCodePaths(pkg);
- oatDir = getOatDir(pkg, pkgSetting);
-
- for (String codePath : codePaths) {
- for (String isa : instructionSets) {
- try {
- mInstaller.deleteOdex(codePath, isa, oatDir);
- } catch (InstallerException e) {
- Log.e(TAG, "Failed deleting oat files for " + codePath, e);
- }
- }
- }
+ mDexManager.deleteOptimizedFiles(ArtUtils.createArtPackageInfo(pkg, pkgSetting));
}
Set<String> getUnusedPackages(long downgradeTimeThresholdMillis) {
diff --git a/services/core/java/com/android/server/pm/dex/ArtPackageInfo.java b/services/core/java/com/android/server/pm/dex/ArtPackageInfo.java
new file mode 100644
index 000000000000..50bf916dceb3
--- /dev/null
+++ b/services/core/java/com/android/server/pm/dex/ArtPackageInfo.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm.dex;
+
+import java.util.List;
+
+/**
+ * Holds package information relevant to ART use cases.
+ */
+public class ArtPackageInfo {
+ private final String mPackageName;
+ private final List<String> mInstructionSets;
+ private final List<String> mCodePaths;
+ // TODO: This should be computed on the fly in PackageDexOptimizer / DexManager, but the
+ // logic is too complicated to do it in a single re-factoring.
+ private final String mOatDir;
+
+ public ArtPackageInfo(
+ String packageName,
+ List<String> instructionSets,
+ List<String> codePaths,
+ String oatDir) {
+ mPackageName = packageName;
+ mInstructionSets = instructionSets;
+ mCodePaths = codePaths;
+ mOatDir = oatDir;
+ }
+
+ public String getPackageName() {
+ return mPackageName;
+ }
+
+ public List<String> getInstructionSets() {
+ return mInstructionSets;
+ }
+
+ public List<String> getCodePaths() {
+ return mCodePaths;
+ }
+
+ public String getOatDir() {
+ return mOatDir;
+ }
+}
diff --git a/services/core/java/com/android/server/pm/dex/ArtUtils.java b/services/core/java/com/android/server/pm/dex/ArtUtils.java
new file mode 100644
index 000000000000..fe6ec0d37ca1
--- /dev/null
+++ b/services/core/java/com/android/server/pm/dex/ArtUtils.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm.dex;
+
+import static com.android.server.pm.InstructionSets.getAppDexInstructionSets;
+
+import android.annotation.NonNull;
+
+import com.android.server.pm.PackageDexOptimizer;
+import com.android.server.pm.PackageSetting;
+import com.android.server.pm.parsing.pkg.AndroidPackage;
+import com.android.server.pm.parsing.pkg.AndroidPackageUtils;
+
+import java.io.File;
+import java.util.Arrays;
+
+/**
+ * Utility class to interface between PM and ART tooling (e.g. DexManager).
+ */
+public final class ArtUtils {
+ private ArtUtils() {
+ }
+
+ /**
+ * Create the ART-representation of package info.
+ */
+ public static ArtPackageInfo createArtPackageInfo(
+ AndroidPackage pkg, PackageSetting pkgSetting) {
+ return new ArtPackageInfo(
+ pkg.getPackageName(),
+ Arrays.asList(getAppDexInstructionSets(
+ AndroidPackageUtils.getPrimaryCpuAbi(pkg, pkgSetting),
+ AndroidPackageUtils.getSecondaryCpuAbi(pkg, pkgSetting))),
+ AndroidPackageUtils.getAllCodePaths(pkg),
+ getOatDir(pkg, pkgSetting));
+ }
+
+ private static String getOatDir(AndroidPackage pkg, @NonNull PackageSetting pkgSetting) {
+ if (!AndroidPackageUtils.canHaveOatDir(pkg,
+ pkgSetting.getPkgState().isUpdatedSystemApp())) {
+ return null;
+ }
+ File codePath = new File(pkg.getCodePath());
+ if (codePath.isDirectory()) {
+ return PackageDexOptimizer.getOatDir(codePath).getAbsolutePath();
+ }
+ return null;
+ }
+
+}
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 6d3de968b036..349561d3f1d1 100644
--- a/services/core/java/com/android/server/pm/dex/DexManager.java
+++ b/services/core/java/com/android/server/pm/dex/DexManager.java
@@ -1015,6 +1015,22 @@ public class DexManager {
return isBtmCritical;
}
+ /**
+ * Deletes all the optimizations files generated by ART.
+ * @param packageInfo the package information.
+ */
+ public void deleteOptimizedFiles(ArtPackageInfo packageInfo) {
+ for (String codePath : packageInfo.getCodePaths()) {
+ for (String isa : packageInfo.getInstructionSets()) {
+ try {
+ mInstaller.deleteOdex(codePath, isa, packageInfo.getOatDir());
+ } catch (InstallerException e) {
+ Log.e(TAG, "Failed deleting oat files for " + codePath, e);
+ }
+ }
+ }
+ }
+
public static class RegisterDexModuleResult {
public RegisterDexModuleResult() {
this(false, null);
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/ConnectivityControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/ConnectivityControllerTest.java
index 7f86faa4b393..d4e4caa906a1 100644
--- a/services/tests/mockingservicestests/src/com/android/server/job/controllers/ConnectivityControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/ConnectivityControllerTest.java
@@ -19,6 +19,7 @@ package com.android.server.job.controllers;
import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_CONGESTED;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED;
import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED;
import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
@@ -626,6 +627,7 @@ public class ConnectivityControllerTest {
private static NetworkCapabilities createCapabilities() {
return new NetworkCapabilities().addCapability(NET_CAPABILITY_INTERNET)
+ .addCapability(NET_CAPABILITY_NOT_VCN_MANAGED)
.addCapability(NET_CAPABILITY_VALIDATED);
}
diff --git a/services/tests/servicestests/src/com/android/server/pm/OWNERS b/services/tests/servicestests/src/com/android/server/pm/OWNERS
index d825dfd7cf00..e15b5f57069c 100644
--- a/services/tests/servicestests/src/com/android/server/pm/OWNERS
+++ b/services/tests/servicestests/src/com/android/server/pm/OWNERS
@@ -1 +1,3 @@
include /services/core/java/com/android/server/pm/OWNERS
+
+per-file *Shortcut* = file:/core/java/android/content/pm/SHORTCUT_OWNERS
diff --git a/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/OWNERS b/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/OWNERS
index 816bc6bba639..e21b66ecb394 100644
--- a/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/OWNERS
+++ b/services/tests/servicestests/src/com/android/server/soundtrigger_middleware/OWNERS
@@ -1 +1 @@
-include /core/java/android/media/soundtrigger/OWNERS
+include /media/java/android/media/soundtrigger_middleware/OWNERS
diff --git a/telephony/java/android/telephony/RadioInterfaceCapabilities.java b/telephony/java/android/telephony/RadioInterfaceCapabilities.java
deleted file mode 100644
index 7c7eb9fbbeb2..000000000000
--- a/telephony/java/android/telephony/RadioInterfaceCapabilities.java
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * 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 android.telephony;
-
-import android.util.ArraySet;
-
-/**
- * Contains the set of supported capabilities that the Radio Interface supports on this device.
- *
- * @hide
- */
-public class RadioInterfaceCapabilities {
-
- private final ArraySet<String> mSupportedCapabilities;
-
-
- public RadioInterfaceCapabilities() {
- mSupportedCapabilities = new ArraySet<>();
- }
-
- /**
- * Marks a capability as supported
- *
- * @param capabilityName the name of the capability
- */
- public void addSupportedCapability(
- @TelephonyManager.RadioInterfaceCapability String capabilityName) {
- mSupportedCapabilities.add(capabilityName);
- }
-
- /**
- * Whether the capability is supported
- *
- * @param capabilityName the name of the capability
- */
- public boolean isSupported(String capabilityName) {
- return mSupportedCapabilities.contains(capabilityName);
- }
-}
diff --git a/telephony/java/android/telephony/SignalStrengthUpdateRequest.java b/telephony/java/android/telephony/SignalStrengthUpdateRequest.java
index af67ed279fab..fe7e5976b132 100644
--- a/telephony/java/android/telephony/SignalStrengthUpdateRequest.java
+++ b/telephony/java/android/telephony/SignalStrengthUpdateRequest.java
@@ -187,7 +187,7 @@ public final class SignalStrengthUpdateRequest implements Parcelable {
return mIsSystemThresholdReportingRequestedWhileIdle;
}
- /*
+ /**
* @return the live token of the request
*
* @hide
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 8bf55ccd9d41..403d1d01903c 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -28,6 +28,7 @@ import android.annotation.IntDef;
import android.annotation.LongDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.RequiresFeature;
import android.annotation.RequiresPermission;
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
@@ -121,12 +122,10 @@ import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
-import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
-import java.util.Set;
import java.util.UUID;
import java.util.concurrent.Executor;
import java.util.function.Consumer;
@@ -8127,6 +8126,11 @@ public class TelephonyManager {
* <p>Requires Permission:
* {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling
* app has carrier privileges (see {@link #hasCarrierPrivileges}).
+ * <p>
+ * If {@link android.telephony.TelephonyManager#isRadioInterfaceCapabilitySupported}
+ * ({@link TelephonyManager#CAPABILITY_ALLOWED_NETWORK_TYPES_USED}) returns true, then
+ * setAllowedNetworkTypesBitmap is used on the radio interface. Otherwise,
+ * setPreferredNetworkTypesBitmap is used instead.
*
* @param subId the id of the subscription to set the preferred network type for.
* @param networkType the preferred network type
@@ -8158,6 +8162,11 @@ public class TelephonyManager {
* <p>Requires Permission:
* {@link android.Manifest.permission#MODIFY_PHONE_STATE MODIFY_PHONE_STATE} or that the calling
* app has carrier privileges (see {@link #hasCarrierPrivileges}).
+ * <p>
+ * If {@link android.telephony.TelephonyManager#isRadioInterfaceCapabilitySupported}
+ * ({@link TelephonyManager#CAPABILITY_ALLOWED_NETWORK_TYPES_USED}) returns true, then
+ * setAllowedNetworkTypesBitmap is used on the radio interface. Otherwise,
+ * setPreferredNetworkTypesBitmap is used instead.
*
* @param networkTypeBitmask The bitmask of preferred network types.
* @return true on success; false on any failure.
@@ -8183,12 +8192,20 @@ public class TelephonyManager {
* Set the allowed network types of the device. This is for carrier or privileged apps to
* enable/disable certain network types on the device. The user preferred network types should
* be set through {@link #setPreferredNetworkTypeBitmask}.
+ * <p>
+ * If {@link android.telephony.TelephonyManager#isRadioInterfaceCapabilitySupported}
+ * ({@link TelephonyManager#CAPABILITY_ALLOWED_NETWORK_TYPES_USED}) returns true, then
+ * setAllowedNetworkTypesBitmap is used on the radio interface. Otherwise,
+ * setPreferredNetworkTypesBitmap is used instead.
*
* @param allowedNetworkTypes The bitmask of allowed network types.
* @return true on success; false on any failure.
* @hide
*/
@RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+ @RequiresFeature(
+ enforcement = "android.telephony.TelephonyManager#isRadioInterfaceCapabilitySupported",
+ value = TelephonyManager.CAPABILITY_ALLOWED_NETWORK_TYPES_USED)
@SystemApi
public boolean setAllowedNetworkTypes(@NetworkTypeBitMask long allowedNetworkTypes) {
try {
@@ -8227,12 +8244,12 @@ public class TelephonyManager {
* {@link #ALLOWED_NETWORK_TYPES_REASON_POWER}
* </ol>
* This API will result in allowing an intersection of allowed network types for all reasons,
- * including the configuration done through {@link setAllowedNetworkTypes}.
- * While this API and {@link setAllowedNetworkTypes} is controlling allowed network types
- * on device, user preference will still be set through {@link #setPreferredNetworkTypeBitmask}.
- * Thus resultant network type configured on modem will be an intersection of the network types
- * from setAllowedNetworkTypesForReason, {@link setAllowedNetworkTypes}
- * and {@link #setPreferredNetworkTypeBitmask}.
+ * including the configuration done through other reasons.
+ * <p>
+ * If {@link android.telephony.TelephonyManager#isRadioInterfaceCapabilitySupported}
+ * ({@link TelephonyManager#CAPABILITY_ALLOWED_NETWORK_TYPES_USED}) returns true, then
+ * setAllowedNetworkTypesBitmap is used on the radio interface. Otherwise,
+ * setPreferredNetworkTypesBitmap is used instead.
*
* @param reason the reason the allowed network type change is taking place
* @param allowedNetworkTypes The bitmask of allowed network types.
@@ -8241,6 +8258,9 @@ public class TelephonyManager {
* @hide
*/
@RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+ @RequiresFeature(
+ enforcement = "android.telephony.TelephonyManager#isRadioInterfaceCapabilitySupported",
+ value = TelephonyManager.CAPABILITY_ALLOWED_NETWORK_TYPES_USED)
public void setAllowedNetworkTypesForReason(@AllowedNetworkTypesReason int reason,
@NetworkTypeBitMask long allowedNetworkTypes) {
if (reason != ALLOWED_NETWORK_TYPES_REASON_POWER) {
@@ -8278,6 +8298,9 @@ public class TelephonyManager {
* @hide
*/
@RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ @RequiresFeature(
+ enforcement = "android.telephony.TelephonyManager#isRadioInterfaceCapabilitySupported",
+ value = TelephonyManager.CAPABILITY_ALLOWED_NETWORK_TYPES_USED)
public @NetworkTypeBitMask long getAllowedNetworkTypesForReason(
@AllowedNetworkTypesReason int reason) {
if (reason != ALLOWED_NETWORK_TYPES_REASON_POWER) {
@@ -14356,10 +14379,24 @@ public class TelephonyManager {
public static final String CAPABILITY_SECONDARY_LINK_BANDWIDTH_VISIBLE =
"CAPABILITY_SECONDARY_LINK_BANDWIDTH_VISIBLE";
+ /**
+ * Indicates whether {@link #setPreferredNetworkType}, {@link
+ * #setPreferredNetworkTypeBitmask}, {@link #setAllowedNetworkTypes} and
+ * {@link #setAllowedNetworkTypesForReason} rely on
+ * setAllowedNetworkTypesBitmap instead of setPreferredNetworkTypesBitmap on the radio
+ * interface.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final String CAPABILITY_ALLOWED_NETWORK_TYPES_USED =
+ "CAPABILITY_ALLOWED_NETWORK_TYPES_USED";
+
/** @hide */
@Retention(RetentionPolicy.SOURCE)
@StringDef(prefix = "CAPABILITY_", value = {
CAPABILITY_SECONDARY_LINK_BANDWIDTH_VISIBLE,
+ CAPABILITY_ALLOWED_NETWORK_TYPES_USED,
})
public @interface RadioInterfaceCapability {}
diff --git a/tests/net/common/java/android/net/CaptivePortalDataTest.kt b/tests/net/common/java/android/net/CaptivePortalDataTest.kt
index ad5bbf220d57..18a93319b271 100644
--- a/tests/net/common/java/android/net/CaptivePortalDataTest.kt
+++ b/tests/net/common/java/android/net/CaptivePortalDataTest.kt
@@ -55,14 +55,14 @@ class CaptivePortalDataTest {
.build()
private val dataFromPasspoint = CaptivePortalData.Builder()
- .setUserPortalUrl(Uri.parse("https://tc.example.com/passpoint"),
- CaptivePortalData.CAPTIVE_PORTAL_DATA_SOURCE_PASSPOINT)
- .setVenueInfoUrl(Uri.parse("https://venue.example.com/passpoint"),
- CaptivePortalData.CAPTIVE_PORTAL_DATA_SOURCE_PASSPOINT)
.setCaptive(true)
.apply {
if (SdkLevel.isAtLeastS()) {
setVenueFriendlyName("venue friendly name")
+ setUserPortalUrl(Uri.parse("https://tc.example.com/passpoint"),
+ CaptivePortalData.CAPTIVE_PORTAL_DATA_SOURCE_PASSPOINT)
+ setVenueInfoUrl(Uri.parse("https://venue.example.com/passpoint"),
+ CaptivePortalData.CAPTIVE_PORTAL_DATA_SOURCE_PASSPOINT)
}
}
.build()
@@ -96,28 +96,28 @@ class CaptivePortalDataTest {
if (SdkLevel.isAtLeastS()) {
assertNotEqualsAfterChange { it.setVenueFriendlyName("another friendly name") }
assertNotEqualsAfterChange { it.setVenueFriendlyName(null) }
- }
- assertEquals(dataFromPasspoint, CaptivePortalData.Builder(dataFromPasspoint).build())
- assertNotEqualsAfterChange { it.setUserPortalUrl(
- Uri.parse("https://tc.example.com/passpoint")) }
- assertNotEqualsAfterChange { it.setUserPortalUrl(
- Uri.parse("https://tc.example.com/passpoint"),
- CaptivePortalData.CAPTIVE_PORTAL_DATA_SOURCE_OTHER) }
- assertNotEqualsAfterChange { it.setUserPortalUrl(
- Uri.parse("https://tc.example.com/other"),
- CaptivePortalData.CAPTIVE_PORTAL_DATA_SOURCE_PASSPOINT) }
- assertNotEqualsAfterChange { it.setUserPortalUrl(
- Uri.parse("https://tc.example.com/passpoint"),
- CaptivePortalData.CAPTIVE_PORTAL_DATA_SOURCE_OTHER) }
- assertNotEqualsAfterChange { it.setVenueInfoUrl(
- Uri.parse("https://venue.example.com/passpoint")) }
- assertNotEqualsAfterChange { it.setVenueInfoUrl(
- Uri.parse("https://venue.example.com/other"),
- CaptivePortalData.CAPTIVE_PORTAL_DATA_SOURCE_PASSPOINT) }
- assertNotEqualsAfterChange { it.setVenueInfoUrl(
- Uri.parse("https://venue.example.com/passpoint"),
- CaptivePortalData.CAPTIVE_PORTAL_DATA_SOURCE_OTHER) }
+ assertEquals(dataFromPasspoint, CaptivePortalData.Builder(dataFromPasspoint).build())
+ assertNotEqualsAfterChange { it.setUserPortalUrl(
+ Uri.parse("https://tc.example.com/passpoint")) }
+ assertNotEqualsAfterChange { it.setUserPortalUrl(
+ Uri.parse("https://tc.example.com/passpoint"),
+ CaptivePortalData.CAPTIVE_PORTAL_DATA_SOURCE_OTHER) }
+ assertNotEqualsAfterChange { it.setUserPortalUrl(
+ Uri.parse("https://tc.example.com/other"),
+ CaptivePortalData.CAPTIVE_PORTAL_DATA_SOURCE_PASSPOINT) }
+ assertNotEqualsAfterChange { it.setUserPortalUrl(
+ Uri.parse("https://tc.example.com/passpoint"),
+ CaptivePortalData.CAPTIVE_PORTAL_DATA_SOURCE_OTHER) }
+ assertNotEqualsAfterChange { it.setVenueInfoUrl(
+ Uri.parse("https://venue.example.com/passpoint")) }
+ assertNotEqualsAfterChange { it.setVenueInfoUrl(
+ Uri.parse("https://venue.example.com/other"),
+ CaptivePortalData.CAPTIVE_PORTAL_DATA_SOURCE_PASSPOINT) }
+ assertNotEqualsAfterChange { it.setVenueInfoUrl(
+ Uri.parse("https://venue.example.com/passpoint"),
+ CaptivePortalData.CAPTIVE_PORTAL_DATA_SOURCE_OTHER) }
+ }
}
@Test
diff --git a/tests/net/integration/util/com/android/server/NetworkAgentWrapper.java b/tests/net/integration/util/com/android/server/NetworkAgentWrapper.java
index dc9e587332cb..e1da3d0ae2b3 100644
--- a/tests/net/integration/util/com/android/server/NetworkAgentWrapper.java
+++ b/tests/net/integration/util/com/android/server/NetworkAgentWrapper.java
@@ -17,6 +17,7 @@
package com.android.server;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VPN;
import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
import static android.net.NetworkCapabilities.TRANSPORT_ETHERNET;
@@ -84,6 +85,7 @@ public class NetworkAgentWrapper implements TestableNetworkCallback.HasNetwork {
final String typeName = ConnectivityManager.getNetworkTypeName(type);
mNetworkCapabilities = (ncTemplate != null) ? ncTemplate : new NetworkCapabilities();
mNetworkCapabilities.addCapability(NET_CAPABILITY_NOT_SUSPENDED);
+ mNetworkCapabilities.addCapability(NET_CAPABILITY_NOT_VCN_MANAGED);
mNetworkCapabilities.addTransportType(transport);
switch (transport) {
case TRANSPORT_ETHERNET:
diff --git a/tests/net/java/android/net/NetworkIdentityTest.kt b/tests/net/java/android/net/NetworkIdentityTest.kt
new file mode 100644
index 000000000000..eb2b85c14578
--- /dev/null
+++ b/tests/net/java/android/net/NetworkIdentityTest.kt
@@ -0,0 +1,54 @@
+/*
+ * 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 android.net
+
+import android.net.NetworkIdentity.OEM_NONE
+import android.net.NetworkIdentity.OEM_PAID
+import android.net.NetworkIdentity.OEM_PRIVATE
+import android.net.NetworkIdentity.getOemBitfield
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+import kotlin.test.assertEquals
+
+@RunWith(JUnit4::class)
+class NetworkIdentityTest {
+ @Test
+ fun testGetOemBitfield() {
+ val oemNone = NetworkCapabilities().apply {
+ setCapability(NetworkCapabilities.NET_CAPABILITY_OEM_PAID, false)
+ setCapability(NetworkCapabilities.NET_CAPABILITY_OEM_PRIVATE, false)
+ }
+ val oemPaid = NetworkCapabilities().apply {
+ setCapability(NetworkCapabilities.NET_CAPABILITY_OEM_PAID, true)
+ setCapability(NetworkCapabilities.NET_CAPABILITY_OEM_PRIVATE, false)
+ }
+ val oemPrivate = NetworkCapabilities().apply {
+ setCapability(NetworkCapabilities.NET_CAPABILITY_OEM_PAID, false)
+ setCapability(NetworkCapabilities.NET_CAPABILITY_OEM_PRIVATE, true)
+ }
+ val oemAll = NetworkCapabilities().apply {
+ setCapability(NetworkCapabilities.NET_CAPABILITY_OEM_PAID, true)
+ setCapability(NetworkCapabilities.NET_CAPABILITY_OEM_PRIVATE, true)
+ }
+
+ assertEquals(getOemBitfield(oemNone), OEM_NONE)
+ assertEquals(getOemBitfield(oemPaid), OEM_PAID)
+ assertEquals(getOemBitfield(oemPrivate), OEM_PRIVATE)
+ assertEquals(getOemBitfield(oemAll), OEM_PAID or OEM_PRIVATE)
+ }
+}
diff --git a/tests/net/java/android/net/NetworkTemplateTest.kt b/tests/net/java/android/net/NetworkTemplateTest.kt
index b39555d15dcb..27224c216db3 100644
--- a/tests/net/java/android/net/NetworkTemplateTest.kt
+++ b/tests/net/java/android/net/NetworkTemplateTest.kt
@@ -20,14 +20,23 @@ import android.content.Context
import android.net.ConnectivityManager.TYPE_MOBILE
import android.net.ConnectivityManager.TYPE_WIFI
import android.net.NetworkIdentity.SUBTYPE_COMBINED
+import android.net.NetworkIdentity.OEM_NONE;
+import android.net.NetworkIdentity.OEM_PAID;
+import android.net.NetworkIdentity.OEM_PRIVATE;
import android.net.NetworkIdentity.buildNetworkIdentity
import android.net.NetworkStats.DEFAULT_NETWORK_ALL
import android.net.NetworkStats.METERED_ALL
import android.net.NetworkStats.ROAMING_ALL
+import android.net.NetworkTemplate.MATCH_ETHERNET
import android.net.NetworkTemplate.MATCH_MOBILE
+import android.net.NetworkTemplate.MATCH_MOBILE_WILDCARD
import android.net.NetworkTemplate.MATCH_WIFI
+import android.net.NetworkTemplate.MATCH_WIFI_WILDCARD
import android.net.NetworkTemplate.NETWORK_TYPE_5G_NSA
import android.net.NetworkTemplate.NETWORK_TYPE_ALL
+import android.net.NetworkTemplate.OEM_MANAGED_ALL
+import android.net.NetworkTemplate.OEM_MANAGED_NO
+import android.net.NetworkTemplate.OEM_MANAGED_YES
import android.net.NetworkTemplate.buildTemplateMobileWithRatType
import android.telephony.TelephonyManager
import com.android.testutils.assertParcelSane
@@ -37,9 +46,11 @@ import org.junit.runner.RunWith
import org.junit.runners.JUnit4
import org.mockito.Mockito.mock
import org.mockito.MockitoAnnotations
+import kotlin.test.assertEquals
import kotlin.test.assertFalse
import kotlin.test.assertNotEquals
import kotlin.test.assertTrue
+import kotlin.test.fail
private const val TEST_IMSI1 = "imsi1"
private const val TEST_IMSI2 = "imsi2"
@@ -57,13 +68,18 @@ class NetworkTemplateTest {
private fun buildNetworkState(
type: Int,
subscriberId: String? = null,
- ssid: String? = null
+ ssid: String? = null,
+ oemManaged: Int = OEM_NONE,
): NetworkState {
val lp = LinkProperties()
val caps = NetworkCapabilities().apply {
setCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED, false)
setCapability(NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING, true)
setSSID(ssid)
+ setCapability(NetworkCapabilities.NET_CAPABILITY_OEM_PAID,
+ (oemManaged and OEM_PAID) == OEM_PAID)
+ setCapability(NetworkCapabilities.NET_CAPABILITY_OEM_PRIVATE,
+ (oemManaged and OEM_PRIVATE) == OEM_PRIVATE)
}
return NetworkState(type, lp, caps, mock(Network::class.java), subscriberId)
}
@@ -136,11 +152,15 @@ class NetworkTemplateTest {
@Test
fun testParcelUnparcel() {
val templateMobile = NetworkTemplate(MATCH_MOBILE, TEST_IMSI1, null, null, METERED_ALL,
- ROAMING_ALL, DEFAULT_NETWORK_ALL, TelephonyManager.NETWORK_TYPE_LTE)
+ ROAMING_ALL, DEFAULT_NETWORK_ALL, TelephonyManager.NETWORK_TYPE_LTE,
+ OEM_MANAGED_ALL)
val templateWifi = NetworkTemplate(MATCH_WIFI, null, null, TEST_SSID1, METERED_ALL,
- ROAMING_ALL, DEFAULT_NETWORK_ALL, 0)
- assertParcelSane(templateMobile, 8)
- assertParcelSane(templateWifi, 8)
+ ROAMING_ALL, DEFAULT_NETWORK_ALL, 0, OEM_MANAGED_ALL)
+ val templateOem = NetworkTemplate(MATCH_MOBILE, null, null, null, METERED_ALL,
+ ROAMING_ALL, DEFAULT_NETWORK_ALL, 0, OEM_MANAGED_YES)
+ assertParcelSane(templateMobile, 9)
+ assertParcelSane(templateWifi, 9)
+ assertParcelSane(templateOem, 9)
}
// Verify NETWORK_TYPE_* constants in NetworkTemplate do not conflict with
@@ -152,4 +172,81 @@ class NetworkTemplateTest {
assertNotEquals(NETWORK_TYPE_5G_NSA, ratType)
}
}
+
+ @Test
+ fun testOemNetworkConstants() {
+ val constantValues = arrayOf(OEM_MANAGED_YES, OEM_MANAGED_ALL, OEM_MANAGED_NO,
+ OEM_PAID, OEM_PRIVATE, OEM_PAID or OEM_PRIVATE)
+
+ // Verify that "not OEM managed network" constants are equal.
+ assertEquals(OEM_MANAGED_NO, OEM_NONE);
+
+ // Verify the constants don't conflict.
+ assertEquals(constantValues.size, constantValues.distinct().count())
+ }
+
+ /**
+ * Helper to enumerate and assert OEM managed wifi and mobile {@code NetworkTemplate}s match
+ * their the appropriate OEM managed {@code NetworkIdentity}s.
+ *
+ * @param networkType {@code TYPE_MOBILE} or {@code TYPE_WIFI}
+ * @param matchType A match rule from {@code NetworkTemplate.MATCH_*} corresponding to the
+ * networkType.
+ * @param subscriberId To be populated with {@code TEST_IMSI*} only if networkType is
+ * {@code TYPE_MOBILE}. May be left as null when matchType is
+ * {@link NetworkTemplate.MATCH_MOBILE_WILDCARD}.
+ * @param templateSsid Top be populated with {@code TEST_SSID*} only if networkType is
+ * {@code TYPE_WIFI}. May be left as null when matchType is
+ * {@link NetworkTemplate.MATCH_WIFI_WILDCARD}.
+ * @param identSsid If networkType is {@code TYPE_WIFI}, this value must *NOT* be null. Provide
+ * one of {@code TEST_SSID*}.
+ */
+ private fun matchOemManagedIdent(networkType: Int, matchType:Int, subscriberId: String? = null,
+ templateSsid: String? = null, identSsid: String? = null) {
+ val oemManagedStates = arrayOf(OEM_NONE, OEM_PAID, OEM_PRIVATE, OEM_PAID or OEM_PRIVATE)
+ // A null subscriberId needs a null matchSubscriberIds argument as well.
+ val matchSubscriberIds = if (subscriberId == null) null else arrayOf(subscriberId)
+
+ val templateOemYes = NetworkTemplate(matchType, subscriberId, matchSubscriberIds,
+ templateSsid, METERED_ALL, ROAMING_ALL, DEFAULT_NETWORK_ALL, NETWORK_TYPE_ALL,
+ OEM_MANAGED_YES)
+ val templateOemAll = NetworkTemplate(matchType, subscriberId, matchSubscriberIds,
+ templateSsid, METERED_ALL, ROAMING_ALL, DEFAULT_NETWORK_ALL, NETWORK_TYPE_ALL,
+ OEM_MANAGED_ALL)
+
+ for (identityOemManagedState in oemManagedStates) {
+ val ident = buildNetworkIdentity(mockContext, buildNetworkState(networkType,
+ subscriberId, identSsid, identityOemManagedState), /*defaultNetwork=*/false,
+ /*subType=*/0)
+
+ // Create a template with each OEM managed type and match it against the NetworkIdentity
+ for (templateOemManagedState in oemManagedStates) {
+ val template = NetworkTemplate(matchType, subscriberId, matchSubscriberIds,
+ templateSsid, METERED_ALL, ROAMING_ALL, DEFAULT_NETWORK_ALL,
+ NETWORK_TYPE_ALL, templateOemManagedState)
+ if (identityOemManagedState == templateOemManagedState) {
+ template.assertMatches(ident)
+ } else {
+ template.assertDoesNotMatch(ident)
+ }
+ }
+ // OEM_MANAGED_ALL ignores OEM state.
+ templateOemAll.assertMatches(ident)
+ if (identityOemManagedState == OEM_NONE) {
+ // OEM_MANAGED_YES matches everything except OEM_NONE.
+ templateOemYes.assertDoesNotMatch(ident)
+ } else {
+ templateOemYes.assertMatches(ident)
+ }
+ }
+ }
+
+ @Test
+ fun testOemManagedMatchesIdent() {
+ matchOemManagedIdent(TYPE_MOBILE, MATCH_MOBILE, subscriberId = TEST_IMSI1)
+ matchOemManagedIdent(TYPE_MOBILE, MATCH_MOBILE_WILDCARD)
+ matchOemManagedIdent(TYPE_WIFI, MATCH_WIFI, templateSsid = TEST_SSID1,
+ identSsid = TEST_SSID1)
+ matchOemManagedIdent(TYPE_WIFI, MATCH_WIFI_WILDCARD, identSsid = TEST_SSID1)
+ }
}
diff --git a/tests/net/java/android/net/VpnTransportInfoTest.java b/tests/net/java/android/net/VpnTransportInfoTest.java
index 866f38c84333..d04c87b29c25 100644
--- a/tests/net/java/android/net/VpnTransportInfoTest.java
+++ b/tests/net/java/android/net/VpnTransportInfoTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2010 The Android Open Source Project
+ * 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.
diff --git a/tests/net/java/android/net/util/MultinetworkPolicyTrackerTest.kt b/tests/net/java/android/net/util/MultinetworkPolicyTrackerTest.kt
new file mode 100644
index 000000000000..9b0cfa9db30f
--- /dev/null
+++ b/tests/net/java/android/net/util/MultinetworkPolicyTrackerTest.kt
@@ -0,0 +1,137 @@
+/*
+ * 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 android.net.util
+
+import android.content.Context
+import android.content.res.Resources
+import android.net.ConnectivityManager.MULTIPATH_PREFERENCE_HANDOVER
+import android.net.ConnectivityManager.MULTIPATH_PREFERENCE_PERFORMANCE
+import android.net.ConnectivityManager.MULTIPATH_PREFERENCE_RELIABILITY
+import android.net.util.MultinetworkPolicyTracker.ActiveDataSubscriptionIdChangedListener
+import android.provider.Settings
+import android.provider.Settings.Global.NETWORK_AVOID_BAD_WIFI
+import android.provider.Settings.Global.NETWORK_METERED_MULTIPATH_PREFERENCE
+import android.telephony.SubscriptionInfo
+import android.telephony.SubscriptionManager
+import android.telephony.TelephonyManager
+import android.test.mock.MockContentResolver
+import androidx.test.filters.SmallTest
+import androidx.test.runner.AndroidJUnit4
+import com.android.internal.R
+import com.android.internal.util.test.FakeSettingsProvider
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertFalse
+import org.junit.Assert.assertTrue
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentCaptor
+import org.mockito.ArgumentMatchers.anyInt
+import org.mockito.ArgumentMatchers.argThat
+import org.mockito.Mockito.any
+import org.mockito.Mockito.doReturn
+import org.mockito.Mockito.mock
+import org.mockito.Mockito.times
+import org.mockito.Mockito.verify
+
+/**
+ * Tests for [MultinetworkPolicyTracker].
+ *
+ * Build, install and run with:
+ * atest android.net.util.MultinetworkPolicyTrackerTest
+ */
+@RunWith(AndroidJUnit4::class)
+@SmallTest
+class MultinetworkPolicyTrackerTest {
+ private val resources = mock(Resources::class.java).also {
+ doReturn(0).`when`(it).getInteger(R.integer.config_networkAvoidBadWifi)
+ }
+ private val telephonyManager = mock(TelephonyManager::class.java)
+ private val subscriptionManager = mock(SubscriptionManager::class.java).also {
+ doReturn(null).`when`(it).getActiveSubscriptionInfo(anyInt())
+ }
+ private val resolver = MockContentResolver().apply {
+ addProvider(Settings.AUTHORITY, FakeSettingsProvider()) }
+ private val context = mock(Context::class.java).also {
+ doReturn(Context.TELEPHONY_SERVICE).`when`(it)
+ .getSystemServiceName(TelephonyManager::class.java)
+ doReturn(telephonyManager).`when`(it).getSystemService(Context.TELEPHONY_SERVICE)
+ doReturn(subscriptionManager).`when`(it)
+ .getSystemService(Context.TELEPHONY_SUBSCRIPTION_SERVICE)
+ doReturn(resolver).`when`(it).contentResolver
+ doReturn(resources).`when`(it).resources
+ doReturn(it).`when`(it).createConfigurationContext(any())
+ Settings.Global.putString(resolver, NETWORK_AVOID_BAD_WIFI, "1")
+ }
+ private val tracker = MultinetworkPolicyTracker(context, null /* handler */)
+
+ private fun assertMultipathPreference(preference: Int) {
+ Settings.Global.putString(resolver, NETWORK_METERED_MULTIPATH_PREFERENCE,
+ preference.toString())
+ tracker.updateMeteredMultipathPreference()
+ assertEquals(preference, tracker.meteredMultipathPreference)
+ }
+
+ @Test
+ fun testUpdateMeteredMultipathPreference() {
+ assertMultipathPreference(MULTIPATH_PREFERENCE_HANDOVER)
+ assertMultipathPreference(MULTIPATH_PREFERENCE_RELIABILITY)
+ assertMultipathPreference(MULTIPATH_PREFERENCE_PERFORMANCE)
+ }
+
+ @Test
+ fun testUpdateAvoidBadWifi() {
+ Settings.Global.putString(resolver, NETWORK_AVOID_BAD_WIFI, "0")
+ assertTrue(tracker.updateAvoidBadWifi())
+ assertFalse(tracker.avoidBadWifi)
+
+ doReturn(1).`when`(resources).getInteger(R.integer.config_networkAvoidBadWifi)
+ assertTrue(tracker.updateAvoidBadWifi())
+ assertTrue(tracker.avoidBadWifi)
+ }
+
+ @Test
+ fun testOnActiveDataSubscriptionIdChanged() {
+ val testSubId = 1000
+ val subscriptionInfo = SubscriptionInfo(testSubId, ""/* iccId */, 1/* iccId */,
+ "TMO"/* displayName */, "TMO"/* carrierName */, 1/* nameSource */, 1/* iconTint */,
+ "123"/* number */, 1/* roaming */, null/* icon */, "310"/* mcc */, "210"/* mnc */,
+ ""/* countryIso */, false/* isEmbedded */, null/* nativeAccessRules */,
+ "1"/* cardString */)
+ doReturn(subscriptionInfo).`when`(subscriptionManager).getActiveSubscriptionInfo(testSubId)
+
+ // Modify avoidBadWifi and meteredMultipathPreference settings value and local variables in
+ // MultinetworkPolicyTracker should be also updated after subId changed.
+ Settings.Global.putString(resolver, NETWORK_AVOID_BAD_WIFI, "0")
+ Settings.Global.putString(resolver, NETWORK_METERED_MULTIPATH_PREFERENCE,
+ MULTIPATH_PREFERENCE_PERFORMANCE.toString())
+
+ val listenerCaptor = ArgumentCaptor.forClass(
+ ActiveDataSubscriptionIdChangedListener::class.java)
+ verify(telephonyManager, times(1))
+ .registerPhoneStateListener(any(), listenerCaptor.capture())
+ val listener = listenerCaptor.value
+ listener.onActiveDataSubscriptionIdChanged(testSubId)
+
+ // Check it get resource value with test sub id.
+ verify(subscriptionManager, times(1)).getActiveSubscriptionInfo(testSubId)
+ verify(context).createConfigurationContext(argThat { it.mcc == 310 && it.mnc == 210 })
+
+ // Check if avoidBadWifi and meteredMultipathPreference values have been updated.
+ assertFalse(tracker.avoidBadWifi)
+ assertEquals(MULTIPATH_PREFERENCE_PERFORMANCE, tracker.meteredMultipathPreference)
+ }
+}
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index fbeebe53fea8..bb822d85b602 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -2798,6 +2798,10 @@ public class ConnectivityServiceTest {
NetworkCapabilities filter = new NetworkCapabilities();
filter.addCapability(capability);
+ // Add NOT_VCN_MANAGED capability into filter unconditionally since some request will add
+ // NOT_VCN_MANAGED automatically but not for NetworkCapabilities,
+ // see {@code NetworkCapabilities#deduceNotVcnManagedCapability} for more details.
+ filter.addCapability(NET_CAPABILITY_NOT_VCN_MANAGED);
final HandlerThread handlerThread = new HandlerThread("testNetworkFactoryRequests");
handlerThread.start();
final MockNetworkFactory testFactory = new MockNetworkFactory(handlerThread.getLooper(),
@@ -4144,6 +4148,7 @@ public class ConnectivityServiceTest {
handlerThread.start();
NetworkCapabilities filter = new NetworkCapabilities()
.addTransportType(TRANSPORT_CELLULAR)
+ .addCapability(NET_CAPABILITY_NOT_VCN_MANAGED)
.addCapability(NET_CAPABILITY_INTERNET);
final MockNetworkFactory testFactory = new MockNetworkFactory(handlerThread.getLooper(),
mServiceContext, "testFactory", filter, mCsHandlerThread);
@@ -6048,6 +6053,7 @@ public class ConnectivityServiceTest {
.addTransportType(TRANSPORT_CELLULAR)
.addCapability(NET_CAPABILITY_INTERNET)
.addCapability(NET_CAPABILITY_NOT_CONGESTED)
+ .addCapability(NET_CAPABILITY_NOT_VCN_MANAGED)
.setLinkDownstreamBandwidthKbps(10);
final NetworkCapabilities wifiNc = new NetworkCapabilities()
.addTransportType(TRANSPORT_WIFI)
@@ -6056,6 +6062,7 @@ public class ConnectivityServiceTest {
.addCapability(NET_CAPABILITY_NOT_ROAMING)
.addCapability(NET_CAPABILITY_NOT_CONGESTED)
.addCapability(NET_CAPABILITY_NOT_SUSPENDED)
+ .addCapability(NET_CAPABILITY_NOT_VCN_MANAGED)
.setLinkUpstreamBandwidthKbps(20);
mCellNetworkAgent.setNetworkCapabilities(cellNc, true /* sendToConnectivityService */);
mWiFiNetworkAgent.setNetworkCapabilities(wifiNc, true /* sendToConnectivityService */);
@@ -7748,19 +7755,13 @@ public class ConnectivityServiceTest {
mWiFiNetworkAgent.removeCapability(testCap);
callbackWithCap.expectAvailableCallbacksValidated(mCellNetworkAgent);
callbackWithoutCap.expectCapabilitiesWithout(testCap, mWiFiNetworkAgent);
- // TODO: Test default network changes for NOT_VCN_MANAGED once the default request has
- // it.
- if (testCap == NET_CAPABILITY_TRUSTED) {
- verify(mMockNetd).networkSetDefault(eq(mCellNetworkAgent.getNetwork().netId));
- reset(mMockNetd);
- }
+ verify(mMockNetd).networkSetDefault(eq(mCellNetworkAgent.getNetwork().netId));
+ reset(mMockNetd);
mCellNetworkAgent.removeCapability(testCap);
callbackWithCap.expectCallback(CallbackEntry.LOST, mCellNetworkAgent);
callbackWithoutCap.assertNoCallback();
- if (testCap == NET_CAPABILITY_TRUSTED) {
- verify(mMockNetd).networkClearDefault();
- }
+ verify(mMockNetd).networkClearDefault();
mCm.unregisterNetworkCallback(callbackWithCap);
mCm.unregisterNetworkCallback(callbackWithoutCap);
diff --git a/tests/net/java/com/android/server/net/NetworkStatsCollectionTest.java b/tests/net/java/com/android/server/net/NetworkStatsCollectionTest.java
index 435c3c0af817..505ff9b6a34b 100644
--- a/tests/net/java/com/android/server/net/NetworkStatsCollectionTest.java
+++ b/tests/net/java/com/android/server/net/NetworkStatsCollectionTest.java
@@ -17,6 +17,7 @@
package com.android.server.net;
import static android.net.ConnectivityManager.TYPE_MOBILE;
+import static android.net.NetworkIdentity.OEM_NONE;
import static android.net.NetworkStats.SET_ALL;
import static android.net.NetworkStats.SET_DEFAULT;
import static android.net.NetworkStats.TAG_NONE;
@@ -213,7 +214,7 @@ public class NetworkStatsCollectionTest {
final NetworkStats.Entry entry = new NetworkStats.Entry();
final NetworkIdentitySet identSet = new NetworkIdentitySet();
identSet.add(new NetworkIdentity(TYPE_MOBILE, TelephonyManager.NETWORK_TYPE_UNKNOWN,
- TEST_IMSI, null, false, true, true));
+ TEST_IMSI, null, false, true, true, OEM_NONE));
int myUid = Process.myUid();
int otherUidInSameUser = Process.myUid() + 1;
@@ -468,7 +469,7 @@ public class NetworkStatsCollectionTest {
final NetworkStatsCollection large = new NetworkStatsCollection(HOUR_IN_MILLIS);
final NetworkIdentitySet ident = new NetworkIdentitySet();
ident.add(new NetworkIdentity(ConnectivityManager.TYPE_MOBILE, -1, TEST_IMSI, null,
- false, true, true));
+ false, true, true, OEM_NONE));
large.recordData(ident, UID_ALL, SET_ALL, TAG_NONE, TIME_A, TIME_B,
new NetworkStats.Entry(12_730_893_164L, 1, 0, 0, 0));
diff --git a/tests/net/java/com/android/server/net/NetworkStatsObserversTest.java b/tests/net/java/com/android/server/net/NetworkStatsObserversTest.java
index 291efc74aa47..9fa1c50423d9 100644
--- a/tests/net/java/com/android/server/net/NetworkStatsObserversTest.java
+++ b/tests/net/java/com/android/server/net/NetworkStatsObserversTest.java
@@ -17,6 +17,7 @@
package com.android.server.net;
import static android.net.ConnectivityManager.TYPE_MOBILE;
+import static android.net.NetworkIdentity.OEM_NONE;
import static android.net.NetworkStats.DEFAULT_NETWORK_NO;
import static android.net.NetworkStats.DEFAULT_NETWORK_YES;
import static android.net.NetworkStats.METERED_NO;
@@ -220,7 +221,7 @@ public class NetworkStatsObserversTest {
identSet.add(new NetworkIdentity(
TYPE_MOBILE, TelephonyManager.NETWORK_TYPE_UNKNOWN,
IMSI_1, null /* networkId */, false /* roaming */, true /* metered */,
- true /* defaultNetwork */));
+ true /* defaultNetwork */, OEM_NONE));
return identSet;
}
diff --git a/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java b/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java
index d644739ea25e..54d6fb9f2c12 100644
--- a/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java
+++ b/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java
@@ -21,6 +21,9 @@ import static android.content.Intent.EXTRA_UID;
import static android.net.ConnectivityManager.TYPE_MOBILE;
import static android.net.ConnectivityManager.TYPE_VPN;
import static android.net.ConnectivityManager.TYPE_WIFI;
+import static android.net.NetworkIdentity.OEM_NONE;
+import static android.net.NetworkIdentity.OEM_PAID;
+import static android.net.NetworkIdentity.OEM_PRIVATE;
import static android.net.NetworkStats.DEFAULT_NETWORK_ALL;
import static android.net.NetworkStats.DEFAULT_NETWORK_NO;
import static android.net.NetworkStats.DEFAULT_NETWORK_YES;
@@ -40,7 +43,10 @@ import static android.net.NetworkStats.TAG_ALL;
import static android.net.NetworkStats.TAG_NONE;
import static android.net.NetworkStats.UID_ALL;
import static android.net.NetworkStatsHistory.FIELD_ALL;
+import static android.net.NetworkTemplate.MATCH_MOBILE_WILDCARD;
import static android.net.NetworkTemplate.NETWORK_TYPE_ALL;
+import static android.net.NetworkTemplate.OEM_MANAGED_NO;
+import static android.net.NetworkTemplate.OEM_MANAGED_YES;
import static android.net.NetworkTemplate.buildTemplateMobileAll;
import static android.net.NetworkTemplate.buildTemplateMobileWithRatType;
import static android.net.NetworkTemplate.buildTemplateWifi;
@@ -643,6 +649,116 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest {
assertUidTotal(template5g, UID_RED, 5L, 13L, 31L, 9L, 2);
}
+ @Test
+ public void testMobileStatsOemManaged() throws Exception {
+ final NetworkTemplate templateOemPaid = new NetworkTemplate(MATCH_MOBILE_WILDCARD,
+ /*subscriberId=*/null, /*matchSubscriberIds=*/null, /*networkId=*/null,
+ METERED_ALL, ROAMING_ALL, DEFAULT_NETWORK_ALL, NETWORK_TYPE_ALL, OEM_PAID);
+
+ final NetworkTemplate templateOemPrivate = new NetworkTemplate(MATCH_MOBILE_WILDCARD,
+ /*subscriberId=*/null, /*matchSubscriberIds=*/null, /*networkId=*/null,
+ METERED_ALL, ROAMING_ALL, DEFAULT_NETWORK_ALL, NETWORK_TYPE_ALL, OEM_PRIVATE);
+
+ final NetworkTemplate templateOemAll = new NetworkTemplate(MATCH_MOBILE_WILDCARD,
+ /*subscriberId=*/null, /*matchSubscriberIds=*/null, /*networkId=*/null,
+ METERED_ALL, ROAMING_ALL, DEFAULT_NETWORK_ALL, NETWORK_TYPE_ALL,
+ OEM_PAID | OEM_PRIVATE);
+
+ final NetworkTemplate templateOemYes = new NetworkTemplate(MATCH_MOBILE_WILDCARD,
+ /*subscriberId=*/null, /*matchSubscriberIds=*/null, /*networkId=*/null,
+ METERED_ALL, ROAMING_ALL, DEFAULT_NETWORK_ALL, NETWORK_TYPE_ALL, OEM_MANAGED_YES);
+
+ final NetworkTemplate templateOemNone = new NetworkTemplate(MATCH_MOBILE_WILDCARD,
+ /*subscriberId=*/null, /*matchSubscriberIds=*/null, /*networkId=*/null,
+ METERED_ALL, ROAMING_ALL, DEFAULT_NETWORK_ALL, NETWORK_TYPE_ALL, OEM_MANAGED_NO);
+
+ // OEM_PAID network comes online.
+ NetworkState[] states = new NetworkState[]{buildOemManagedMobileState(IMSI_1, false,
+ new int[]{NetworkCapabilities.NET_CAPABILITY_OEM_PAID})};
+ expectNetworkStatsSummary(buildEmptyStats());
+ expectNetworkStatsUidDetail(buildEmptyStats());
+ mService.forceUpdateIfaces(NETWORKS_MOBILE, states, getActiveIface(states),
+ new UnderlyingNetworkInfo[0]);
+
+ // Create some traffic.
+ incrementCurrentTime(MINUTE_IN_MILLIS);
+ expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1)
+ .addEntry(new NetworkStats.Entry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE,
+ 36L, 41L, 24L, 96L, 0L)));
+ forcePollAndWaitForIdle();
+
+ // OEM_PRIVATE network comes online.
+ states = new NetworkState[]{buildOemManagedMobileState(IMSI_1, false,
+ new int[]{NetworkCapabilities.NET_CAPABILITY_OEM_PRIVATE})};
+ expectNetworkStatsSummary(buildEmptyStats());
+ expectNetworkStatsUidDetail(buildEmptyStats());
+ mService.forceUpdateIfaces(NETWORKS_MOBILE, states, getActiveIface(states),
+ new UnderlyingNetworkInfo[0]);
+
+ // Create some traffic.
+ incrementCurrentTime(MINUTE_IN_MILLIS);
+ expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1)
+ .addEntry(new NetworkStats.Entry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE,
+ 49L, 71L, 72L, 48L, 0L)));
+ forcePollAndWaitForIdle();
+
+ // OEM_PAID + OEM_PRIVATE network comes online.
+ states = new NetworkState[]{buildOemManagedMobileState(IMSI_1, false,
+ new int[]{NetworkCapabilities.NET_CAPABILITY_OEM_PRIVATE,
+ NetworkCapabilities.NET_CAPABILITY_OEM_PAID})};
+ expectNetworkStatsSummary(buildEmptyStats());
+ expectNetworkStatsUidDetail(buildEmptyStats());
+ mService.forceUpdateIfaces(NETWORKS_MOBILE, states, getActiveIface(states),
+ new UnderlyingNetworkInfo[0]);
+
+ // Create some traffic.
+ incrementCurrentTime(MINUTE_IN_MILLIS);
+ expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1)
+ .addEntry(new NetworkStats.Entry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE,
+ 57L, 86L, 83L, 93L, 0L)));
+ forcePollAndWaitForIdle();
+
+ // OEM_NONE network comes online.
+ states = new NetworkState[]{buildOemManagedMobileState(IMSI_1, false, new int[]{})};
+ expectNetworkStatsSummary(buildEmptyStats());
+ expectNetworkStatsUidDetail(buildEmptyStats());
+ mService.forceUpdateIfaces(NETWORKS_MOBILE, states, getActiveIface(states),
+ new UnderlyingNetworkInfo[0]);
+
+ // Create some traffic.
+ incrementCurrentTime(MINUTE_IN_MILLIS);
+ expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1)
+ .addEntry(new NetworkStats.Entry(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE,
+ 29L, 73L, 34L, 31L, 0L)));
+ forcePollAndWaitForIdle();
+
+ // Verify OEM_PAID template gets only relevant stats.
+ assertUidTotal(templateOemPaid, UID_RED, 36L, 41L, 24L, 96L, 0);
+
+ // Verify OEM_PRIVATE template gets only relevant stats.
+ assertUidTotal(templateOemPrivate, UID_RED, 49L, 71L, 72L, 48L, 0);
+
+ // Verify OEM_PAID + OEM_PRIVATE template gets only relevant stats.
+ assertUidTotal(templateOemAll, UID_RED, 57L, 86L, 83L, 93L, 0);
+
+ // Verify OEM_NONE sees only non-OEM managed stats.
+ assertUidTotal(templateOemNone, UID_RED, 29L, 73L, 34L, 31L, 0);
+
+ // Verify OEM_MANAGED_YES sees all OEM managed stats.
+ assertUidTotal(templateOemYes, UID_RED,
+ 36L + 49L + 57L,
+ 41L + 71L + 86L,
+ 24L + 72L + 83L,
+ 96L + 48L + 93L, 0);
+
+ // Verify ALL_MOBILE template gets both OEM managed and non-OEM managed stats.
+ assertUidTotal(sTemplateImsi1, UID_RED,
+ 36L + 49L + 57L + 29L,
+ 41L + 71L + 86L + 73L,
+ 24L + 72L + 83L + 34L,
+ 96L + 48L + 93L + 31L, 0);
+ }
+
// TODO: support per IMSI state
private void setMobileRatTypeAndWaitForIdle(int ratType) {
when(mNetworkStatsSubscriptionsMonitor.getRatTypeForSubscriberId(anyString()))
@@ -1488,6 +1604,20 @@ public class NetworkStatsServiceTest extends NetworkStatsBaseTest {
return new NetworkState(TYPE_VPN, prop, new NetworkCapabilities(), VPN_NETWORK, null);
}
+ private static NetworkState buildOemManagedMobileState(String subscriberId, boolean isRoaming,
+ int[] oemNetCapabilities) {
+ final LinkProperties prop = new LinkProperties();
+ prop.setInterfaceName(TEST_IFACE);
+ final NetworkCapabilities capabilities = new NetworkCapabilities();
+ capabilities.setCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED, false);
+ capabilities.setCapability(NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING, !isRoaming);
+ for (int nc : oemNetCapabilities) {
+ capabilities.setCapability(nc, true);
+ }
+ capabilities.addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR);
+ return new NetworkState(TYPE_MOBILE, prop, capabilities, MOBILE_NETWORK, subscriberId);
+ }
+
private long getElapsedRealtime() {
return mElapsedRealtime;
}