Merge "Add another OptionsBuilder constructor with the source type as one of its parameter."
diff --git a/boot/Android.bp b/boot/Android.bp
index 3caede4..09cd0d4 100644
--- a/boot/Android.bp
+++ b/boot/Android.bp
@@ -52,9 +52,45 @@
module: "art-bootclasspath-fragment",
},
{
+ apex: "com.android.conscrypt",
+ module: "com.android.conscrypt-bootclasspath-fragment",
+ },
+ {
apex: "com.android.i18n",
module: "i18n-bootclasspath-fragment",
},
+ {
+ apex: "com.android.ipsec",
+ module: "com.android.ipsec-bootclasspath-fragment",
+ },
+ {
+ apex: "com.android.media",
+ module: "com.android.media-bootclasspath-fragment",
+ },
+ {
+ apex: "com.android.mediaprovider",
+ module: "com.android.mediaprovider-bootclasspath-fragment",
+ },
+ {
+ apex: "com.android.os.statsd",
+ module: "com.android.os.statsd-bootclasspath-fragment",
+ },
+ {
+ apex: "com.android.permission",
+ module: "com.android.permission-bootclasspath-fragment",
+ },
+ {
+ apex: "com.android.sdkext",
+ module: "com.android.sdkext-bootclasspath-fragment",
+ },
+ {
+ apex: "com.android.tethering",
+ module: "com.android.tethering-bootclasspath-fragment",
+ },
+ {
+ apex: "com.android.wifi",
+ module: "com.android.wifi-bootclasspath-fragment",
+ },
],
// Additional information needed by hidden api processing.
diff --git a/core/java/android/net/Ikev2VpnProfile.java b/core/java/android/net/Ikev2VpnProfile.java
index cc1312b..b18e9be 100644
--- a/core/java/android/net/Ikev2VpnProfile.java
+++ b/core/java/android/net/Ikev2VpnProfile.java
@@ -16,6 +16,16 @@
package android.net;
+import static android.net.IpSecAlgorithm.AUTH_AES_CMAC;
+import static android.net.IpSecAlgorithm.AUTH_AES_XCBC;
+import static android.net.IpSecAlgorithm.AUTH_CRYPT_AES_GCM;
+import static android.net.IpSecAlgorithm.AUTH_CRYPT_CHACHA20_POLY1305;
+import static android.net.IpSecAlgorithm.AUTH_HMAC_SHA256;
+import static android.net.IpSecAlgorithm.AUTH_HMAC_SHA384;
+import static android.net.IpSecAlgorithm.AUTH_HMAC_SHA512;
+import static android.net.IpSecAlgorithm.CRYPT_AES_CBC;
+import static android.net.IpSecAlgorithm.CRYPT_AES_CTR;
+
import static com.android.internal.annotations.VisibleForTesting.Visibility;
import static com.android.internal.util.Preconditions.checkStringNotEmpty;
import static com.android.net.module.util.NetworkStackConstants.IPV6_MIN_MTU;
@@ -70,13 +80,28 @@
private static final String EMPTY_CERT = "";
/** @hide */
- public static final List<String> DEFAULT_ALGORITHMS =
- Collections.unmodifiableList(Arrays.asList(
- IpSecAlgorithm.CRYPT_AES_CBC,
- IpSecAlgorithm.AUTH_HMAC_SHA256,
- IpSecAlgorithm.AUTH_HMAC_SHA384,
- IpSecAlgorithm.AUTH_HMAC_SHA512,
- IpSecAlgorithm.AUTH_CRYPT_AES_GCM));
+ public static final List<String> DEFAULT_ALGORITHMS;
+
+ private static void addAlgorithmIfSupported(List<String> algorithms, String ipSecAlgoName) {
+ if (IpSecAlgorithm.getSupportedAlgorithms().contains(ipSecAlgoName)) {
+ algorithms.add(ipSecAlgoName);
+ }
+ }
+
+ static {
+ final List<String> algorithms = new ArrayList<>();
+ addAlgorithmIfSupported(algorithms, CRYPT_AES_CBC);
+ addAlgorithmIfSupported(algorithms, CRYPT_AES_CTR);
+ addAlgorithmIfSupported(algorithms, AUTH_HMAC_SHA256);
+ addAlgorithmIfSupported(algorithms, AUTH_HMAC_SHA384);
+ addAlgorithmIfSupported(algorithms, AUTH_HMAC_SHA512);
+ addAlgorithmIfSupported(algorithms, AUTH_AES_XCBC);
+ addAlgorithmIfSupported(algorithms, AUTH_AES_CMAC);
+ addAlgorithmIfSupported(algorithms, AUTH_CRYPT_AES_GCM);
+ addAlgorithmIfSupported(algorithms, AUTH_CRYPT_CHACHA20_POLY1305);
+
+ DEFAULT_ALGORITHMS = Collections.unmodifiableList(algorithms);
+ }
@NonNull private final String mServerAddr;
@NonNull private final String mUserIdentity;
@@ -195,8 +220,6 @@
* @param allowedAlgorithms The list to be validated
*/
private static void validateAllowedAlgorithms(@NonNull List<String> algorithmNames) {
- VpnProfile.validateAllowedAlgorithms(algorithmNames);
-
// First, make sure no insecure algorithms were proposed.
if (algorithmNames.contains(IpSecAlgorithm.AUTH_HMAC_MD5)
|| algorithmNames.contains(IpSecAlgorithm.AUTH_HMAC_SHA1)) {
diff --git a/core/java/com/android/internal/net/VpnProfile.java b/core/java/com/android/internal/net/VpnProfile.java
index b7170d8..6e1d3ce 100644
--- a/core/java/com/android/internal/net/VpnProfile.java
+++ b/core/java/com/android/internal/net/VpnProfile.java
@@ -30,7 +30,10 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.net.module.util.ProxyUtils;
+import java.io.UnsupportedEncodingException;
import java.net.InetAddress;
+import java.net.URLDecoder;
+import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
@@ -74,6 +77,9 @@
private static final String ENCODED_NULL_PROXY_INFO = "\0\0\0\0";
+ /** Default URL encoding. */
+ private static final String DEFAULT_ENCODING = StandardCharsets.UTF_8.name();
+
// Entity fields.
@UnsupportedAppUsage
public final String key; // -1
@@ -129,9 +135,6 @@
/**
* The list of allowable algorithms.
- *
- * <p>This list is validated in the setter to ensure that encoding characters (list, value
- * delimiters) are not present in the algorithm names. See {@link #validateAllowedAlgorithms()}
*/
private List<String> mAllowedAlgorithms = new ArrayList<>(); // 19
public boolean isBypassable = false; // 20
@@ -196,11 +199,8 @@
*
* @param allowedAlgorithms the list of allowable algorithms, as listed in {@link
* IpSecAlgorithm}.
- * @throws IllegalArgumentException if any delimiters are used in algorithm names. See {@link
- * #VALUE_DELIMITER} and {@link LIST_DELIMITER}.
*/
public void setAllowedAlgorithms(List<String> allowedAlgorithms) {
- validateAllowedAlgorithms(allowedAlgorithms);
mAllowedAlgorithms = allowedAlgorithms;
}
@@ -297,7 +297,11 @@
// Either all must be present, or none must be.
if (values.length >= 24) {
- profile.mAllowedAlgorithms = Arrays.asList(values[19].split(LIST_DELIMITER));
+ profile.mAllowedAlgorithms = new ArrayList<>();
+ for (String algo : Arrays.asList(values[19].split(LIST_DELIMITER))) {
+ profile.mAllowedAlgorithms.add(URLDecoder.decode(algo, DEFAULT_ENCODING));
+ }
+
profile.isBypassable = Boolean.parseBoolean(values[20]);
profile.isMetered = Boolean.parseBoolean(values[21]);
profile.maxMtu = Integer.parseInt(values[22]);
@@ -348,7 +352,19 @@
builder.append(ENCODED_NULL_PROXY_INFO);
}
- builder.append(VALUE_DELIMITER).append(String.join(LIST_DELIMITER, mAllowedAlgorithms));
+ final List<String> encodedAlgoNames = new ArrayList<>();
+
+ try {
+ for (String algo : mAllowedAlgorithms) {
+ encodedAlgoNames.add(URLEncoder.encode(algo, DEFAULT_ENCODING));
+ }
+ } catch (UnsupportedEncodingException e) {
+ // Unexpected error
+ throw new IllegalStateException("Failed to encode algorithms.", e);
+ }
+
+ builder.append(VALUE_DELIMITER).append(String.join(LIST_DELIMITER, encodedAlgoNames));
+
builder.append(VALUE_DELIMITER).append(isBypassable);
builder.append(VALUE_DELIMITER).append(isMetered);
builder.append(VALUE_DELIMITER).append(maxMtu);
@@ -425,20 +441,6 @@
return true;
}
- /**
- * Validates that the provided list of algorithms does not contain illegal characters.
- *
- * @param allowedAlgorithms The list to be validated
- */
- public static void validateAllowedAlgorithms(List<String> allowedAlgorithms) {
- for (final String alg : allowedAlgorithms) {
- if (alg.contains(VALUE_DELIMITER) || alg.contains(LIST_DELIMITER)) {
- throw new IllegalArgumentException(
- "Algorithm contained illegal ('\0' or ',') character");
- }
- }
- }
-
/** Generates a hashcode over the VpnProfile. */
@Override
public int hashCode() {
diff --git a/core/java/com/android/internal/os/BinderDeathDispatcher.java b/core/java/com/android/internal/os/BinderDeathDispatcher.java
index 0c93f7f..038707e 100644
--- a/core/java/com/android/internal/os/BinderDeathDispatcher.java
+++ b/core/java/com/android/internal/os/BinderDeathDispatcher.java
@@ -63,6 +63,10 @@
@Override
public void binderDied() {
+ }
+
+ @Override
+ public void binderDied(IBinder who) {
final ArraySet<DeathRecipient> copy;
synchronized (mLock) {
copy = mRecipients;
@@ -77,7 +81,7 @@
// Let's call it without holding the lock.
final int size = copy.size();
for (int i = 0; i < size; i++) {
- copy.valueAt(i).binderDied();
+ copy.valueAt(i).binderDied(who);
}
}
}
diff --git a/core/java/com/android/internal/os/BinderLatencyObserver.java b/core/java/com/android/internal/os/BinderLatencyObserver.java
index 59cc66d..4ca59be 100644
--- a/core/java/com/android/internal/os/BinderLatencyObserver.java
+++ b/core/java/com/android/internal/os/BinderLatencyObserver.java
@@ -16,22 +16,37 @@
package com.android.internal.os;
+import static com.android.internal.os.BinderLatencyProto.Dims.SYSTEM_SERVER;
+
import android.annotation.Nullable;
import android.os.Binder;
+import android.os.Handler;
+import android.os.Looper;
import android.os.SystemClock;
import android.util.ArrayMap;
import android.util.Slog;
+import android.util.proto.ProtoOutputStream;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.os.BinderInternal.CallSession;
+import com.android.internal.os.BinderLatencyProto.ApiStats;
+import com.android.internal.os.BinderLatencyProto.Dims;
+import com.android.internal.os.BinderLatencyProto.RepeatedApiStats;
+import com.android.internal.util.FrameworkStatsLog;
import java.util.Random;
/** Collects statistics about Binder call latency per calling API and method. */
public class BinderLatencyObserver {
private static final String TAG = "BinderLatencyObserver";
+ private static final int MAX_ATOM_SIZE_BYTES = 4064;
+ // Be conservative and leave 1K space for the last histogram so we don't go over the size limit.
+ private static final int LAST_HISTOGRAM_BUFFER_SIZE_BYTES = 1000;
+
+ // Latency observer parameters.
public static final int PERIODIC_SAMPLING_INTERVAL_DEFAULT = 10;
+ public static final int STATSD_PUSH_INTERVAL_MINUTES_DEFAULT = 360;
// Histogram buckets parameters.
public static final int BUCKET_COUNT_DEFAULT = 100;
@@ -50,20 +65,124 @@
private int mFirstBucketSize = FIRST_BUCKET_SIZE_DEFAULT;
private float mBucketScaleFactor = BUCKET_SCALE_FACTOR_DEFAULT;
+ private int mStatsdPushIntervalMinutes = STATSD_PUSH_INTERVAL_MINUTES_DEFAULT;
+
private final Random mRandom;
private BinderLatencyBuckets mLatencyBuckets;
+ private final Handler mLatencyObserverHandler;
+
+ private Runnable mLatencyObserverRunnable = new Runnable() {
+ @Override
+ public void run() {
+ // Schedule the next push.
+ noteLatencyDelayed();
+
+ ArrayMap<LatencyDims, int[]> histogramMap;
+ synchronized (mLock) {
+ // Copy the histograms map so we don't use the lock for longer than needed.
+ histogramMap = new ArrayMap<>(mLatencyHistograms);
+ mLatencyHistograms.clear();
+ }
+
+ BinderTransactionNameResolver resolver = new BinderTransactionNameResolver();
+ ProtoOutputStream proto = new ProtoOutputStream();
+ int histogramsWritten = 0;
+
+ for (LatencyDims dims : histogramMap.keySet()) {
+ // Start a new atom if the next histogram risks going over the atom size limit.
+ if (proto.getRawSize() + LAST_HISTOGRAM_BUFFER_SIZE_BYTES > getMaxAtomSizeBytes()) {
+ if (histogramsWritten > 0) {
+ writeAtomToStatsd(proto);
+ }
+ proto = new ProtoOutputStream();
+ histogramsWritten = 0;
+ }
+
+ String transactionName = resolver.getMethodName(
+ dims.getBinderClass(), dims.getTransactionCode());
+ fillApiStatsProto(proto, dims, transactionName, histogramMap.get(dims));
+ histogramsWritten++;
+ }
+ // Push the final atom.
+ if (histogramsWritten > 0) {
+ writeAtomToStatsd(proto);
+ }
+ }
+ };
+
+ private void fillApiStatsProto(
+ ProtoOutputStream proto, LatencyDims dims, String transactionName, int[] histogram) {
+ // Find the part of the histogram to write.
+ int firstNonEmptyBucket = 0;
+ for (int i = 0; i < mBucketCount; i++) {
+ if (histogram[i] != 0) {
+ firstNonEmptyBucket = i;
+ break;
+ }
+ }
+ int lastNonEmptyBucket = mBucketCount - 1;
+ for (int i = mBucketCount - 1; i >= 0; i--) {
+ if (histogram[i] != 0) {
+ lastNonEmptyBucket = i;
+ break;
+ }
+ }
+
+ // Start a new ApiStats proto.
+ long apiStatsToken = proto.start(RepeatedApiStats.API_STATS);
+
+ // Write the dims.
+ long dimsToken = proto.start(ApiStats.DIMS);
+ proto.write(Dims.PROCESS_SOURCE, SYSTEM_SERVER);
+ proto.write(Dims.SERVICE_CLASS_NAME, dims.getBinderClass().getName());
+ proto.write(Dims.SERVICE_METHOD_NAME, transactionName);
+ proto.end(dimsToken);
+
+ // Write the histogram.
+ proto.write(ApiStats.FIRST_BUCKET_INDEX, firstNonEmptyBucket);
+ for (int i = firstNonEmptyBucket; i <= lastNonEmptyBucket; i++) {
+ proto.write(ApiStats.BUCKETS, histogram[i]);
+ }
+
+ proto.end(apiStatsToken);
+ }
+
+ protected int getMaxAtomSizeBytes() {
+ return MAX_ATOM_SIZE_BYTES;
+ }
+
+ protected void writeAtomToStatsd(ProtoOutputStream atom) {
+ FrameworkStatsLog.write(
+ FrameworkStatsLog.BINDER_LATENCY_REPORTED,
+ atom.getBytes(),
+ mPeriodicSamplingInterval,
+ 1);
+ }
+
+ private void noteLatencyDelayed() {
+ mLatencyObserverHandler.removeCallbacks(mLatencyObserverRunnable);
+ mLatencyObserverHandler.postDelayed(mLatencyObserverRunnable,
+ mStatsdPushIntervalMinutes * 60 * 1000);
+ }
+
/** Injector for {@link BinderLatencyObserver}. */
public static class Injector {
public Random getRandomGenerator() {
return new Random();
}
+
+ public Handler getHandler() {
+ return new Handler(Looper.getMainLooper());
+ }
}
public BinderLatencyObserver(Injector injector) {
mRandom = injector.getRandomGenerator();
+ mLatencyObserverHandler = injector.getHandler();
mLatencyBuckets = new BinderLatencyBuckets(
mBucketCount, mFirstBucketSize, mBucketScaleFactor);
+ noteLatencyDelayed();
}
/** Should be called when a Binder call completes, will store latency data. */
@@ -73,7 +192,8 @@
}
LatencyDims dims = new LatencyDims(s.binderClass, s.transactionCode);
- long callDuration = getElapsedRealtimeMicro() - s.timeStarted;
+ long elapsedTimeMicro = getElapsedRealtimeMicro();
+ long callDuration = elapsedTimeMicro - s.timeStarted;
// Find the bucket this sample should go to.
int bucketIdx = mLatencyBuckets.sampleToBucket(
@@ -117,6 +237,22 @@
}
}
+ /** Updates the statsd push interval. */
+ public void setPushInterval(int pushIntervalMinutes) {
+ if (pushIntervalMinutes <= 0) {
+ Slog.w(TAG, "Ignored invalid push interval (value must be positive): "
+ + pushIntervalMinutes);
+ return;
+ }
+
+ synchronized (mLock) {
+ if (pushIntervalMinutes != mStatsdPushIntervalMinutes) {
+ mStatsdPushIntervalMinutes = pushIntervalMinutes;
+ reset();
+ }
+ }
+ }
+
/** Updates the histogram buckets parameters. */
public void setHistogramBucketsParams(
int bucketCount, int firstBucketSize, float bucketScaleFactor) {
@@ -138,6 +274,7 @@
synchronized (mLock) {
mLatencyHistograms.clear();
}
+ noteLatencyDelayed();
}
/** Container for binder latency information. */
@@ -187,4 +324,9 @@
public ArrayMap<LatencyDims, int[]> getLatencyHistograms() {
return mLatencyHistograms;
}
+
+ @VisibleForTesting
+ public Runnable getStatsdPushRunnable() {
+ return mLatencyObserverRunnable;
+ }
}
diff --git a/core/java/com/android/internal/os/TEST_MAPPING b/core/java/com/android/internal/os/TEST_MAPPING
index f44b9fb..fdc3a9ee 100644
--- a/core/java/com/android/internal/os/TEST_MAPPING
+++ b/core/java/com/android/internal/os/TEST_MAPPING
@@ -1,6 +1,16 @@
{
"presubmit": [
{
+ "file_patterns": [
+ "BinderDeathDispatcher\\.java"
+ ],
+ "name": "FrameworksCoreTests",
+ "options": [
+ { "include-filter": "com.android.internal.os.BinderDeathDispatcherTest" },
+ { "exclude-annotation": "com.android.internal.os.SkipPresubmit" }
+ ]
+ },
+ {
"name": "FrameworksCoreTests",
"options": [
{
diff --git a/core/jni/com_android_internal_net_NetworkUtilsInternal.cpp b/core/jni/com_android_internal_net_NetworkUtilsInternal.cpp
index 980e12d..83e2f2b 100644
--- a/core/jni/com_android_internal_net_NetworkUtilsInternal.cpp
+++ b/core/jni/com_android_internal_net_NetworkUtilsInternal.cpp
@@ -31,7 +31,7 @@
}
static jboolean android_net_utils_protectFromVpnWithFd(JNIEnv *env, jobject thiz, jobject javaFd) {
- return android_net_utils_protectFromVpn(env, thiz, AFileDescriptor_getFD(env, javaFd));
+ return android_net_utils_protectFromVpn(env, thiz, AFileDescriptor_getFd(env, javaFd));
}
static const JNINativeMethod gNetworkUtilMethods[] = {
diff --git a/core/proto/android/internal/binder_latency.proto b/core/proto/android/internal/binder_latency.proto
new file mode 100644
index 0000000..e32c3e3
--- /dev/null
+++ b/core/proto/android/internal/binder_latency.proto
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+syntax = "proto2";
+package com.android.internal.os;
+
+option java_outer_classname = "BinderLatencyProto";
+
+/**
+ * RepeatedApiStats proto from atoms.proto, duplicated here so that it's
+ * accessible in the build.
+ * Must be kept in sync with the version in atoms.proto.
+ */
+
+message RepeatedApiStats {
+ repeated ApiStats api_stats = 1;
+}
+
+message Dims {
+ enum ProcessSource {
+ UNKNOWN_PROCESS_SOURCE = 0;
+ SYSTEM_SERVER = 1;
+ TELEPHONY = 2;
+ }
+
+ enum ServiceClassName {
+ UNKNOWN_CLASS = 0;
+ }
+ enum ServiceMethodName {
+ UNKNOWN_METHOD = 0;
+ }
+
+ // Required.
+ optional ProcessSource process_source = 1;
+
+ // The class name of the API making the call to Binder. Enum value
+ // is preferred as uses much less data to store.
+ // This field does not contain PII.
+ oneof service_class {
+ ServiceClassName service_class_name_as_enum = 2;
+ string service_class_name = 3;
+ }
+
+ // Method name of the API call. It can also be a transaction code if we
+ // cannot resolve it to a name. See Binder#getTransactionName. Enum value
+ // is preferred as uses much less data to store.
+ // This field does not contain PII.
+ oneof service_method {
+ ServiceMethodName service_method_name_as_enum = 4;
+ string service_method_name = 5;
+ }
+}
+
+message ApiStats {
+ // required.
+ optional Dims dims = 1;
+
+ // Indicates the first bucket that had any data. Allows omitting any empty
+ // buckets at the start of the bucket list and thus save on data size.
+ optional int32 first_bucket_index = 2;
+ // Stores the count of samples for each bucket. The number of buckets and
+ // their sizes are controlled server side with a flag.
+ repeated int32 buckets = 3;
+}
\ No newline at end of file
diff --git a/core/tests/coretests/src/com/android/internal/os/BinderDeathDispatcherTest.java b/core/tests/coretests/src/com/android/internal/os/BinderDeathDispatcherTest.java
index 942045c..8310333 100644
--- a/core/tests/coretests/src/com/android/internal/os/BinderDeathDispatcherTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BinderDeathDispatcherTest.java
@@ -17,6 +17,7 @@
import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.times;
@@ -31,14 +32,14 @@
import android.os.ResultReceiver;
import android.os.ShellCallback;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+
import org.junit.Test;
import org.junit.runner.RunWith;
import java.io.FileDescriptor;
-import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
-
@SmallTest
@RunWith(AndroidJUnit4.class)
public class BinderDeathDispatcherTest {
@@ -120,7 +121,7 @@
public void die() {
isAlive = false;
if (mRecipient != null) {
- mRecipient.binderDied();
+ mRecipient.binderDied(this);
}
mRecipient = null;
}
@@ -227,33 +228,33 @@
// Kill the targets.
t1.die();
- verify(r1, times(1)).binderDied();
- verify(r2, times(1)).binderDied();
- verify(r3, times(1)).binderDied();
- verify(r4, times(0)).binderDied();
- verify(r5, times(0)).binderDied();
+ verify(r1, times(1)).binderDied(t1);
+ verify(r2, times(1)).binderDied(t1);
+ verify(r3, times(1)).binderDied(t1);
+ verify(r4, times(0)).binderDied(any());
+ verify(r5, times(0)).binderDied(any());
assertThat(d.getTargetsForTest().size()).isEqualTo(2);
reset(r1, r2, r3, r4, r5);
t2.die();
- verify(r1, times(1)).binderDied();
- verify(r2, times(0)).binderDied();
- verify(r3, times(0)).binderDied();
- verify(r4, times(0)).binderDied();
- verify(r5, times(0)).binderDied();
+ verify(r1, times(1)).binderDied(t2);
+ verify(r2, times(0)).binderDied(any());
+ verify(r3, times(0)).binderDied(any());
+ verify(r4, times(0)).binderDied(any());
+ verify(r5, times(0)).binderDied(any());
assertThat(d.getTargetsForTest().size()).isEqualTo(1);
reset(r1, r2, r3, r4, r5);
t3.die();
- verify(r1, times(0)).binderDied();
- verify(r2, times(0)).binderDied();
- verify(r3, times(1)).binderDied();
- verify(r4, times(0)).binderDied();
- verify(r5, times(1)).binderDied();
+ verify(r1, times(0)).binderDied(any());
+ verify(r2, times(0)).binderDied(any());
+ verify(r3, times(1)).binderDied(t3);
+ verify(r4, times(0)).binderDied(any());
+ verify(r5, times(1)).binderDied(t3);
assertThat(d.getTargetsForTest().size()).isEqualTo(0);
@@ -262,4 +263,27 @@
assertThat(d.getTargetsForTest().size()).isEqualTo(0);
}
+
+ @Test
+ public void duplicateRegistrations() {
+ BinderDeathDispatcher<MyTarget> d = new BinderDeathDispatcher<>();
+
+ MyTarget t1 = new MyTarget();
+
+ DeathRecipient r1 = mock(DeathRecipient.class);
+ DeathRecipient r2 = mock(DeathRecipient.class);
+
+ for (int i = 0; i < 5; i++) {
+ assertThat(d.linkToDeath(t1, r1)).isEqualTo(1);
+ }
+ assertThat(d.linkToDeath(t1, r2)).isEqualTo(2);
+
+ t1.die();
+ verify(r1, times(1)).binderDied(t1);
+ verify(r2, times(1)).binderDied(t1);
+
+ d.unlinkToDeath(t1, r1);
+ d.unlinkToDeath(t1, r2);
+ assertThat(d.getTargetsForTest()).isEmpty();
+ }
}
diff --git a/core/tests/coretests/src/com/android/internal/os/BinderLatencyObserverTest.java b/core/tests/coretests/src/com/android/internal/os/BinderLatencyObserverTest.java
index f65fb95..bf87683 100644
--- a/core/tests/coretests/src/com/android/internal/os/BinderLatencyObserverTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BinderLatencyObserverTest.java
@@ -16,6 +16,8 @@
package com.android.internal.os;
+import static com.android.internal.os.BinderLatencyProto.Dims.SYSTEM_SERVER;
+
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertEquals;
@@ -23,16 +25,21 @@
import android.os.Binder;
import android.platform.test.annotations.Presubmit;
import android.util.ArrayMap;
+import android.util.proto.ProtoOutputStream;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
import com.android.internal.os.BinderInternal.CallSession;
import com.android.internal.os.BinderLatencyObserver.LatencyDims;
+import com.android.internal.os.BinderLatencyProto.ApiStats;
+import com.android.internal.os.BinderLatencyProto.Dims;
+import com.android.internal.os.BinderLatencyProto.RepeatedApiStats;
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.util.ArrayList;
import java.util.Arrays;
import java.util.Random;
@@ -49,11 +56,17 @@
CallSession callSession = new CallSession();
callSession.binderClass = binder.getClass();
callSession.transactionCode = 1;
+
+ blo.setElapsedTime(2);
blo.callEnded(callSession);
+ blo.setElapsedTime(4);
blo.callEnded(callSession);
+ blo.setElapsedTime(6);
blo.callEnded(callSession);
callSession.transactionCode = 2;
+ blo.setElapsedTime(8);
blo.callEnded(callSession);
+ blo.setElapsedTime(10);
blo.callEnded(callSession);
ArrayMap<LatencyDims, int[]> latencyHistograms = blo.getLatencyHistograms();
@@ -74,8 +87,10 @@
CallSession callSession = new CallSession();
callSession.binderClass = binder.getClass();
callSession.transactionCode = 1;
+ blo.setElapsedTime(2);
blo.callEnded(callSession);
callSession.transactionCode = 2;
+ blo.setElapsedTime(4);
blo.callEnded(callSession);
ArrayMap<LatencyDims, int[]> latencyHistograms = blo.getLatencyHistograms();
@@ -89,13 +104,13 @@
@Test
public void testTooCallLengthOverflow() {
TestBinderLatencyObserver blo = new TestBinderLatencyObserver();
- blo.setElapsedTime(2L + (long) Integer.MAX_VALUE);
blo.setHistogramBucketsParams(5, 5, 1.125f);
Binder binder = new Binder();
CallSession callSession = new CallSession();
callSession.binderClass = binder.getClass();
callSession.transactionCode = 1;
+ blo.setElapsedTime(2L + (long) Integer.MAX_VALUE);
blo.callEnded(callSession);
// The long call should be capped to maxint (to not overflow) and placed in the last bucket.
@@ -114,6 +129,7 @@
CallSession callSession = new CallSession();
callSession.binderClass = binder.getClass();
callSession.transactionCode = 1;
+ blo.setElapsedTime(2);
blo.callEnded(callSession);
LatencyDims dims = new LatencyDims(binder.getClass(), 1);
@@ -122,14 +138,111 @@
assertThat(blo.getLatencyHistograms().get(dims))
.asList().containsExactly(Integer.MAX_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE);
// Try to add another sample.
+ blo.setElapsedTime(2);
blo.callEnded(callSession);
// Make sure the buckets don't overflow.
assertThat(blo.getLatencyHistograms().get(dims))
.asList().containsExactly(Integer.MAX_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE);
}
+ @Test
+ public void testSingleAtomPush() {
+ TestBinderLatencyObserver blo = new TestBinderLatencyObserver();
+
+ Binder binder = new Binder();
+ CallSession callSession = new CallSession();
+ callSession.binderClass = binder.getClass();
+ callSession.transactionCode = 1;
+ blo.setElapsedTime(7);
+ blo.callEnded(callSession);
+ blo.callEnded(callSession);
+ blo.setElapsedTime(8);
+ blo.callEnded(callSession);
+
+ // Trigger the statsd push.
+ blo.getStatsdPushRunnable().run();
+
+ ProtoOutputStream expectedProto = new ProtoOutputStream();
+ long apiStatsToken = expectedProto.start(RepeatedApiStats.API_STATS);
+ long dimsToken = expectedProto.start(ApiStats.DIMS);
+ expectedProto.write(Dims.PROCESS_SOURCE, SYSTEM_SERVER);
+ expectedProto.write(Dims.SERVICE_CLASS_NAME, binder.getClass().getName());
+ expectedProto.write(Dims.SERVICE_METHOD_NAME, "1");
+ expectedProto.end(dimsToken);
+ expectedProto.write(ApiStats.FIRST_BUCKET_INDEX, 3);
+ expectedProto.write(ApiStats.BUCKETS, 2);
+ expectedProto.write(ApiStats.BUCKETS, 1);
+ expectedProto.end(apiStatsToken);
+
+ assertThat(blo.getWrittenAtoms())
+ .containsExactly(Arrays.toString(expectedProto.getBytes()));
+ }
+
+ @Test
+ public void testMultipleAtomPush() {
+ TestBinderLatencyObserver blo = new TestBinderLatencyObserver();
+
+ BinderTransactionNameResolver resolver = new BinderTransactionNameResolver();
+
+
+ Binder binder = new Binder();
+ CallSession callSession = new CallSession();
+ callSession.binderClass = binder.getClass();
+ callSession.transactionCode = 1;
+ blo.setElapsedTime(1);
+ blo.callEnded(callSession);
+ callSession.transactionCode = 2;
+ blo.setElapsedTime(5);
+ blo.callEnded(callSession);
+ callSession.transactionCode = 3;
+ blo.callEnded(callSession);
+
+ // Trigger the statsd push.
+ blo.getStatsdPushRunnable().run();
+
+ ProtoOutputStream expectedProto1 = new ProtoOutputStream();
+ long apiStatsToken = expectedProto1.start(RepeatedApiStats.API_STATS);
+ long dimsToken = expectedProto1.start(ApiStats.DIMS);
+ expectedProto1.write(Dims.PROCESS_SOURCE, SYSTEM_SERVER);
+ expectedProto1.write(Dims.SERVICE_CLASS_NAME, binder.getClass().getName());
+ expectedProto1.write(Dims.SERVICE_METHOD_NAME, "1");
+ expectedProto1.end(dimsToken);
+ expectedProto1.write(ApiStats.FIRST_BUCKET_INDEX, 0);
+ expectedProto1.write(ApiStats.BUCKETS, 1);
+ expectedProto1.end(apiStatsToken);
+
+ apiStatsToken = expectedProto1.start(RepeatedApiStats.API_STATS);
+ dimsToken = expectedProto1.start(ApiStats.DIMS);
+ expectedProto1.write(Dims.PROCESS_SOURCE, SYSTEM_SERVER);
+ expectedProto1.write(Dims.SERVICE_CLASS_NAME, binder.getClass().getName());
+ expectedProto1.write(Dims.SERVICE_METHOD_NAME, "2");
+ expectedProto1.end(dimsToken);
+ expectedProto1.write(ApiStats.FIRST_BUCKET_INDEX, 1);
+ expectedProto1.write(ApiStats.BUCKETS, 1);
+ expectedProto1.end(apiStatsToken);
+
+ ProtoOutputStream expectedProto2 = new ProtoOutputStream();
+ apiStatsToken = expectedProto2.start(RepeatedApiStats.API_STATS);
+ dimsToken = expectedProto2.start(ApiStats.DIMS);
+ expectedProto2.write(Dims.PROCESS_SOURCE, SYSTEM_SERVER);
+ expectedProto2.write(Dims.SERVICE_CLASS_NAME, binder.getClass().getName());
+ expectedProto2.write(Dims.SERVICE_METHOD_NAME, "3");
+ expectedProto2.end(dimsToken);
+ expectedProto2.write(ApiStats.FIRST_BUCKET_INDEX, 1);
+ expectedProto2.write(ApiStats.BUCKETS, 1);
+ expectedProto2.end(apiStatsToken);
+
+ // Each ApiStats is around ~60 bytes so only two should fit in an atom.
+ assertThat(blo.getWrittenAtoms())
+ .containsExactly(
+ Arrays.toString(expectedProto1.getBytes()),
+ Arrays.toString(expectedProto2.getBytes()))
+ .inOrder();
+ }
+
public static class TestBinderLatencyObserver extends BinderLatencyObserver {
private long mElapsedTime = 0;
+ private ArrayList<String> mWrittenAtoms;
TestBinderLatencyObserver() {
// Make random generator not random.
@@ -145,16 +258,30 @@
}
});
setSamplingInterval(1);
+ mWrittenAtoms = new ArrayList<>();
}
@Override
protected long getElapsedRealtimeMicro() {
- mElapsedTime += 2;
return mElapsedTime;
}
+ @Override
+ protected int getMaxAtomSizeBytes() {
+ return 1100;
+ }
+
+ @Override
+ protected void writeAtomToStatsd(ProtoOutputStream atom) {
+ mWrittenAtoms.add(Arrays.toString(atom.getBytes()));
+ }
+
public void setElapsedTime(long time) {
mElapsedTime = time;
}
+
+ public ArrayList<String> getWrittenAtoms() {
+ return mWrittenAtoms;
+ }
}
}
diff --git a/packages/Connectivity/OWNERS b/packages/Connectivity/OWNERS
index 48e54da..4f1e3e5 100644
--- a/packages/Connectivity/OWNERS
+++ b/packages/Connectivity/OWNERS
@@ -1,3 +1,3 @@
-set noparent
-
-include platform/frameworks/base:/services/core/java/com/android/server/net/OWNERS
+# Placing an OWNERS block to perform migration
+# detailed in b/186628461
+baligh@google.com
diff --git a/packages/Connectivity/framework/jni/android_net_NetworkUtils.cpp b/packages/Connectivity/framework/jni/android_net_NetworkUtils.cpp
index 48e262a..f17baf9 100644
--- a/packages/Connectivity/framework/jni/android_net_NetworkUtils.cpp
+++ b/packages/Connectivity/framework/jni/android_net_NetworkUtils.cpp
@@ -76,7 +76,7 @@
filter_code,
};
- int fd = AFileDescriptor_getFD(env, javaFd);
+ int fd = AFileDescriptor_getFd(env, javaFd);
if (setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &filter, sizeof(filter)) != 0) {
jniThrowExceptionFmt(env, "java/net/SocketException",
"setsockopt(SO_ATTACH_FILTER): %s", strerror(errno));
@@ -86,7 +86,7 @@
static void android_net_utils_detachBPFFilter(JNIEnv *env, jobject clazz, jobject javaFd)
{
int optval_ignored = 0;
- int fd = AFileDescriptor_getFD(env, javaFd);
+ int fd = AFileDescriptor_getFd(env, javaFd);
if (setsockopt(fd, SOL_SOCKET, SO_DETACH_FILTER, &optval_ignored, sizeof(optval_ignored)) !=
0) {
jniThrowExceptionFmt(env, "java/net/SocketException",
@@ -112,7 +112,7 @@
static jint android_net_utils_bindSocketToNetwork(JNIEnv *env, jobject thiz, jobject javaFd,
jint netId) {
- return setNetworkForSocket(netId, AFileDescriptor_getFD(env, javaFd));
+ return setNetworkForSocket(netId, AFileDescriptor_getFd(env, javaFd));
}
static bool checkLenAndCopy(JNIEnv* env, const jbyteArray& addr, int len, void* dst)
@@ -160,7 +160,7 @@
}
static jobject android_net_utils_resNetworkResult(JNIEnv *env, jobject thiz, jobject javaFd) {
- int fd = AFileDescriptor_getFD(env, javaFd);
+ int fd = AFileDescriptor_getFd(env, javaFd);
int rcode;
std::vector<uint8_t> buf(MAXPACKETSIZE, 0);
@@ -187,7 +187,7 @@
}
static void android_net_utils_resNetworkCancel(JNIEnv *env, jobject thiz, jobject javaFd) {
- int fd = AFileDescriptor_getFD(env, javaFd);
+ int fd = AFileDescriptor_getFd(env, javaFd);
resNetworkCancel(fd);
jniSetFileDescriptorOfFD(env, javaFd, -1);
}
@@ -213,7 +213,7 @@
return NULL;
}
- int fd = AFileDescriptor_getFD(env, javaFd);
+ int fd = AFileDescriptor_getFd(env, javaFd);
struct tcp_repair_window trw = {};
socklen_t size = sizeof(trw);
diff --git a/packages/Connectivity/tests/unit/java/android/net/Ikev2VpnProfileTest.java b/packages/Connectivity/tests/unit/java/android/net/Ikev2VpnProfileTest.java
index 1abd39a..0707ef3 100644
--- a/packages/Connectivity/tests/unit/java/android/net/Ikev2VpnProfileTest.java
+++ b/packages/Connectivity/tests/unit/java/android/net/Ikev2VpnProfileTest.java
@@ -29,8 +29,8 @@
import androidx.test.runner.AndroidJUnit4;
import com.android.internal.net.VpnProfile;
-import com.android.net.module.util.ProxyUtils;
import com.android.internal.org.bouncycastle.x509.X509V1CertificateGenerator;
+import com.android.net.module.util.ProxyUtils;
import org.junit.Before;
import org.junit.Test;
@@ -170,7 +170,10 @@
final Ikev2VpnProfile.Builder builder = getBuilderWithDefaultOptions();
builder.setAuthPsk(PSK_BYTES);
- List<String> allowedAlgorithms = Arrays.asList(IpSecAlgorithm.AUTH_CRYPT_AES_GCM);
+ List<String> allowedAlgorithms =
+ Arrays.asList(
+ IpSecAlgorithm.AUTH_CRYPT_AES_GCM,
+ IpSecAlgorithm.AUTH_CRYPT_CHACHA20_POLY1305);
builder.setAllowedAlgorithms(allowedAlgorithms);
final Ikev2VpnProfile profile = builder.build();
@@ -183,7 +186,12 @@
builder.setAuthPsk(PSK_BYTES);
List<String> allowedAlgorithms =
- Arrays.asList(IpSecAlgorithm.AUTH_HMAC_SHA512, IpSecAlgorithm.CRYPT_AES_CBC);
+ Arrays.asList(
+ IpSecAlgorithm.AUTH_HMAC_SHA512,
+ IpSecAlgorithm.AUTH_AES_XCBC,
+ IpSecAlgorithm.AUTH_AES_CMAC,
+ IpSecAlgorithm.CRYPT_AES_CBC,
+ IpSecAlgorithm.CRYPT_AES_CTR);
builder.setAllowedAlgorithms(allowedAlgorithms);
final Ikev2VpnProfile profile = builder.build();
diff --git a/packages/Connectivity/tests/unit/java/com/android/internal/net/VpnProfileTest.java b/packages/Connectivity/tests/unit/java/com/android/internal/net/VpnProfileTest.java
index 46597d1..cb0f071 100644
--- a/packages/Connectivity/tests/unit/java/com/android/internal/net/VpnProfileTest.java
+++ b/packages/Connectivity/tests/unit/java/com/android/internal/net/VpnProfileTest.java
@@ -23,7 +23,6 @@
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
import android.net.IpSecAlgorithm;
@@ -97,6 +96,7 @@
p.setAllowedAlgorithms(
Arrays.asList(
IpSecAlgorithm.AUTH_CRYPT_AES_GCM,
+ IpSecAlgorithm.AUTH_CRYPT_CHACHA20_POLY1305,
IpSecAlgorithm.AUTH_HMAC_SHA512,
IpSecAlgorithm.CRYPT_AES_CBC));
p.isBypassable = true;
@@ -126,30 +126,6 @@
}
@Test
- public void testSetInvalidAlgorithmValueDelimiter() {
- final VpnProfile profile = getSampleIkev2Profile(DUMMY_PROFILE_KEY);
-
- try {
- profile.setAllowedAlgorithms(
- Arrays.asList("test" + VpnProfile.VALUE_DELIMITER + "test"));
- fail("Expected failure due to value separator in algorithm name");
- } catch (IllegalArgumentException expected) {
- }
- }
-
- @Test
- public void testSetInvalidAlgorithmListDelimiter() {
- final VpnProfile profile = getSampleIkev2Profile(DUMMY_PROFILE_KEY);
-
- try {
- profile.setAllowedAlgorithms(
- Arrays.asList("test" + VpnProfile.LIST_DELIMITER + "test"));
- fail("Expected failure due to value separator in algorithm name");
- } catch (IllegalArgumentException expected) {
- }
- }
-
- @Test
public void testEncodeDecode() {
final VpnProfile profile = getSampleIkev2Profile(DUMMY_PROFILE_KEY);
final VpnProfile decoded = VpnProfile.decode(DUMMY_PROFILE_KEY, profile.encode());
diff --git a/services/core/java/com/android/server/AlarmManagerService.java b/services/core/java/com/android/server/AlarmManagerService.java
index 7cdcc01..781c354 100644
--- a/services/core/java/com/android/server/AlarmManagerService.java
+++ b/services/core/java/com/android/server/AlarmManagerService.java
@@ -91,6 +91,7 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.os.BinderDeathDispatcher;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.FrameworkStatsLog;
@@ -176,6 +177,8 @@
.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING
| Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
+ private static final BinderDeathDispatcher<IAlarmListener> sListenerDeathDispatcher =
+ new BinderDeathDispatcher<>();
final LocalLog mLog = new LocalLog(TAG);
AppOpsManager mAppOps;
@@ -1701,9 +1704,8 @@
}
if (directReceiver != null) {
- try {
- directReceiver.asBinder().linkToDeath(mListenerDeathRecipient, 0);
- } catch (RemoteException e) {
+ if (sListenerDeathDispatcher.linkToDeath(directReceiver, mListenerDeathRecipient)
+ <= 0) {
Slog.w(TAG, "Dropping unreachable alarm listener " + listenerTag);
return;
}
@@ -2464,6 +2466,10 @@
pw.println("]");
pw.println();
+ pw.println("Listener death dispatcher state:");
+ sListenerDeathDispatcher.dump(pw, " ");
+ pw.println();
+
if (mLog.dump(pw, " Recent problems", " ")) {
pw.println();
}
diff --git a/services/core/java/com/android/server/BinderCallsStatsService.java b/services/core/java/com/android/server/BinderCallsStatsService.java
index 339ca84..65bb21c 100644
--- a/services/core/java/com/android/server/BinderCallsStatsService.java
+++ b/services/core/java/com/android/server/BinderCallsStatsService.java
@@ -134,6 +134,8 @@
private static final String SETTINGS_COLLECT_LATENCY_DATA_KEY = "collect_Latency_data";
private static final String SETTINGS_LATENCY_OBSERVER_SAMPLING_INTERVAL_KEY =
"latency_observer_sampling_interval";
+ private static final String SETTINGS_LATENCY_OBSERVER_PUSH_INTERVAL_MINUTES_KEY =
+ "latency_observer_push_interval_minutes";
private static final String SETTINGS_LATENCY_HISTOGRAM_BUCKET_COUNT_KEY =
"latency_histogram_bucket_count";
private static final String SETTINGS_LATENCY_HISTOGRAM_FIRST_BUCKET_SIZE_KEY =
@@ -211,7 +213,9 @@
mParser.getFloat(
SETTINGS_LATENCY_HISTOGRAM_BUCKET_SCALE_FACTOR_KEY,
BinderLatencyObserver.BUCKET_SCALE_FACTOR_DEFAULT));
-
+ binderLatencyObserver.setPushInterval(mParser.getInt(
+ SETTINGS_LATENCY_OBSERVER_PUSH_INTERVAL_MINUTES_KEY,
+ BinderLatencyObserver.STATSD_PUSH_INTERVAL_MINUTES_DEFAULT));
final boolean enabled =
mParser.getBoolean(SETTINGS_ENABLED_KEY, BinderCallsStats.ENABLED_DEFAULT);
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 5122be2..011a356 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -4060,13 +4060,19 @@
final int max = SystemProperties.getInt("tombstoned.max_anr_count", 64);
final long now = System.currentTimeMillis();
- Arrays.sort(files, Comparator.comparingLong(File::lastModified).reversed());
- for (int i = 0; i < files.length; ++i) {
- if (i > max || (now - files[i].lastModified()) > DAY_IN_MILLIS) {
- if (!files[i].delete()) {
- Slog.w(TAG, "Unable to prune stale trace file: " + files[i]);
+ try {
+ Arrays.sort(files, Comparator.comparingLong(File::lastModified).reversed());
+ for (int i = 0; i < files.length; ++i) {
+ if (i > max || (now - files[i].lastModified()) > DAY_IN_MILLIS) {
+ if (!files[i].delete()) {
+ Slog.w(TAG, "Unable to prune stale trace file: " + files[i]);
+ }
}
}
+ } catch (IllegalArgumentException e) {
+ // The modification times changed while we were sorting. Bail...
+ // https://issuetracker.google.com/169836837
+ Slog.w(TAG, "tombstone modification times changed while sorting; not pruning", e);
}
}
diff --git a/services/core/java/com/android/server/connectivity/VpnIkev2Utils.java b/services/core/java/com/android/server/connectivity/VpnIkev2Utils.java
index 47eb3eb..a0a596d 100644
--- a/services/core/java/com/android/server/connectivity/VpnIkev2Utils.java
+++ b/services/core/java/com/android/server/connectivity/VpnIkev2Utils.java
@@ -18,10 +18,16 @@
import static android.net.ConnectivityManager.NetworkCallback;
import static android.net.ipsec.ike.SaProposal.DH_GROUP_2048_BIT_MODP;
+import static android.net.ipsec.ike.SaProposal.DH_GROUP_3072_BIT_MODP;
+import static android.net.ipsec.ike.SaProposal.DH_GROUP_4096_BIT_MODP;
+import static android.net.ipsec.ike.SaProposal.DH_GROUP_CURVE_25519;
import static android.net.ipsec.ike.SaProposal.ENCRYPTION_ALGORITHM_AES_CBC;
+import static android.net.ipsec.ike.SaProposal.ENCRYPTION_ALGORITHM_AES_CTR;
import static android.net.ipsec.ike.SaProposal.ENCRYPTION_ALGORITHM_AES_GCM_12;
import static android.net.ipsec.ike.SaProposal.ENCRYPTION_ALGORITHM_AES_GCM_16;
import static android.net.ipsec.ike.SaProposal.ENCRYPTION_ALGORITHM_AES_GCM_8;
+import static android.net.ipsec.ike.SaProposal.ENCRYPTION_ALGORITHM_CHACHA20_POLY1305;
+import static android.net.ipsec.ike.SaProposal.INTEGRITY_ALGORITHM_AES_CMAC_96;
import static android.net.ipsec.ike.SaProposal.INTEGRITY_ALGORITHM_AES_XCBC_96;
import static android.net.ipsec.ike.SaProposal.INTEGRITY_ALGORITHM_HMAC_SHA2_256_128;
import static android.net.ipsec.ike.SaProposal.INTEGRITY_ALGORITHM_HMAC_SHA2_384_192;
@@ -29,8 +35,13 @@
import static android.net.ipsec.ike.SaProposal.KEY_LEN_AES_128;
import static android.net.ipsec.ike.SaProposal.KEY_LEN_AES_192;
import static android.net.ipsec.ike.SaProposal.KEY_LEN_AES_256;
+import static android.net.ipsec.ike.SaProposal.KEY_LEN_UNUSED;
+import static android.net.ipsec.ike.SaProposal.PSEUDORANDOM_FUNCTION_AES128_CMAC;
import static android.net.ipsec.ike.SaProposal.PSEUDORANDOM_FUNCTION_AES128_XCBC;
import static android.net.ipsec.ike.SaProposal.PSEUDORANDOM_FUNCTION_HMAC_SHA1;
+import static android.net.ipsec.ike.SaProposal.PSEUDORANDOM_FUNCTION_SHA2_256;
+import static android.net.ipsec.ike.SaProposal.PSEUDORANDOM_FUNCTION_SHA2_384;
+import static android.net.ipsec.ike.SaProposal.PSEUDORANDOM_FUNCTION_SHA2_512;
import android.annotation.NonNull;
import android.content.Context;
@@ -84,12 +95,6 @@
public class VpnIkev2Utils {
private static final String TAG = VpnIkev2Utils.class.getSimpleName();
- // TODO: Use IKE library exposed constants when @SystemApi is updated.
- /** IANA-defined 3072 group for use in IKEv2 */
- private static final int DH_GROUP_3072_BIT_MODP = 15;
- /** IANA-defined 4096 group for use in IKEv2 */
- private static final int DH_GROUP_4096_BIT_MODP = 16;
-
static IkeSessionParams buildIkeSessionParams(
@NonNull Context context, @NonNull Ikev2VpnProfile profile, @NonNull Network network) {
final IkeIdentification localId = parseIkeIdentification(profile.getUserIdentity());
@@ -154,12 +159,14 @@
// TODO: Add ability to filter this when IKEv2 API is made Public API
final List<IkeSaProposal> proposals = new ArrayList<>();
- // Encryption Algorithms: Currently only AES_CBC is supported.
final IkeSaProposal.Builder normalModeBuilder = new IkeSaProposal.Builder();
- // Currently only AES_CBC is supported.
+ // Add normal mode encryption algorithms
+ normalModeBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_CTR, KEY_LEN_AES_256);
normalModeBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_CBC, KEY_LEN_AES_256);
+ normalModeBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_CTR, KEY_LEN_AES_192);
normalModeBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_CBC, KEY_LEN_AES_192);
+ normalModeBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_CTR, KEY_LEN_AES_128);
normalModeBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_CBC, KEY_LEN_AES_128);
// Authentication/Integrity Algorithms
@@ -167,9 +174,11 @@
normalModeBuilder.addIntegrityAlgorithm(INTEGRITY_ALGORITHM_HMAC_SHA2_384_192);
normalModeBuilder.addIntegrityAlgorithm(INTEGRITY_ALGORITHM_HMAC_SHA2_256_128);
normalModeBuilder.addIntegrityAlgorithm(INTEGRITY_ALGORITHM_AES_XCBC_96);
+ normalModeBuilder.addIntegrityAlgorithm(INTEGRITY_ALGORITHM_AES_CMAC_96);
// Add AEAD options
final IkeSaProposal.Builder aeadBuilder = new IkeSaProposal.Builder();
+ aeadBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_CHACHA20_POLY1305, KEY_LEN_UNUSED);
aeadBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_GCM_16, KEY_LEN_AES_256);
aeadBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_GCM_12, KEY_LEN_AES_256);
aeadBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_GCM_8, KEY_LEN_AES_256);
@@ -183,9 +192,17 @@
// Add dh, prf for both builders
for (final IkeSaProposal.Builder builder : Arrays.asList(normalModeBuilder, aeadBuilder)) {
builder.addDhGroup(DH_GROUP_4096_BIT_MODP);
+
+ // Curve25519 has the same security strength as MODP 3072 and cost less bytes
+ builder.addDhGroup(DH_GROUP_CURVE_25519);
+
builder.addDhGroup(DH_GROUP_3072_BIT_MODP);
builder.addDhGroup(DH_GROUP_2048_BIT_MODP);
+ builder.addPseudorandomFunction(PSEUDORANDOM_FUNCTION_SHA2_512);
+ builder.addPseudorandomFunction(PSEUDORANDOM_FUNCTION_SHA2_384);
+ builder.addPseudorandomFunction(PSEUDORANDOM_FUNCTION_SHA2_256);
builder.addPseudorandomFunction(PSEUDORANDOM_FUNCTION_AES128_XCBC);
+ builder.addPseudorandomFunction(PSEUDORANDOM_FUNCTION_AES128_CMAC);
builder.addPseudorandomFunction(PSEUDORANDOM_FUNCTION_HMAC_SHA1);
}
@@ -198,15 +215,23 @@
private static List<ChildSaProposal> getChildSaProposals(List<String> allowedAlgorithms) {
final List<ChildSaProposal> proposals = new ArrayList<>();
+ final List<Integer> aesKeyLenOptions =
+ Arrays.asList(KEY_LEN_AES_256, KEY_LEN_AES_192, KEY_LEN_AES_128);
+
// Add non-AEAD options
if (Ikev2VpnProfile.hasNormalModeAlgorithms(allowedAlgorithms)) {
final ChildSaProposal.Builder normalModeBuilder = new ChildSaProposal.Builder();
// Encryption Algorithms:
- // AES-CBC is currently the only supported encryption algorithm.
- normalModeBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_CBC, KEY_LEN_AES_256);
- normalModeBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_CBC, KEY_LEN_AES_192);
- normalModeBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_CBC, KEY_LEN_AES_128);
+ // AES-CBC and AES_CTR are currently the only supported encryption algorithms.
+ for (int len : aesKeyLenOptions) {
+ if (allowedAlgorithms.contains(IpSecAlgorithm.CRYPT_AES_CTR)) {
+ normalModeBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_CTR, len);
+ }
+ if (allowedAlgorithms.contains(IpSecAlgorithm.CRYPT_AES_CBC)) {
+ normalModeBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_CBC, len);
+ }
+ }
// Authentication/Integrity Algorithms:
// Guaranteed by Ikev2VpnProfile constructor to contain at least one of these.
@@ -219,6 +244,12 @@
if (allowedAlgorithms.contains(IpSecAlgorithm.AUTH_HMAC_SHA256)) {
normalModeBuilder.addIntegrityAlgorithm(INTEGRITY_ALGORITHM_HMAC_SHA2_256_128);
}
+ if (allowedAlgorithms.contains(IpSecAlgorithm.AUTH_AES_XCBC)) {
+ normalModeBuilder.addIntegrityAlgorithm(INTEGRITY_ALGORITHM_AES_XCBC_96);
+ }
+ if (allowedAlgorithms.contains(IpSecAlgorithm.AUTH_AES_CMAC)) {
+ normalModeBuilder.addIntegrityAlgorithm(INTEGRITY_ALGORITHM_AES_CMAC_96);
+ }
ChildSaProposal proposal = normalModeBuilder.build();
if (proposal.getIntegrityAlgorithms().isEmpty()) {
@@ -233,16 +264,27 @@
if (Ikev2VpnProfile.hasAeadAlgorithms(allowedAlgorithms)) {
final ChildSaProposal.Builder aeadBuilder = new ChildSaProposal.Builder();
- // AES-GCM is currently the only supported AEAD algorithm
- aeadBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_GCM_16, KEY_LEN_AES_256);
- aeadBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_GCM_12, KEY_LEN_AES_256);
- aeadBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_GCM_8, KEY_LEN_AES_256);
- aeadBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_GCM_16, KEY_LEN_AES_192);
- aeadBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_GCM_12, KEY_LEN_AES_192);
- aeadBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_GCM_8, KEY_LEN_AES_192);
- aeadBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_GCM_16, KEY_LEN_AES_128);
- aeadBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_GCM_12, KEY_LEN_AES_128);
- aeadBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_GCM_8, KEY_LEN_AES_128);
+ if (allowedAlgorithms.contains(IpSecAlgorithm.AUTH_CRYPT_CHACHA20_POLY1305)) {
+ aeadBuilder.addEncryptionAlgorithm(
+ ENCRYPTION_ALGORITHM_CHACHA20_POLY1305, KEY_LEN_UNUSED);
+ }
+ if (allowedAlgorithms.contains(IpSecAlgorithm.AUTH_CRYPT_AES_GCM)) {
+ aeadBuilder.addEncryptionAlgorithm(
+ ENCRYPTION_ALGORITHM_AES_GCM_16, KEY_LEN_AES_256);
+ aeadBuilder.addEncryptionAlgorithm(
+ ENCRYPTION_ALGORITHM_AES_GCM_12, KEY_LEN_AES_256);
+ aeadBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_GCM_8, KEY_LEN_AES_256);
+ aeadBuilder.addEncryptionAlgorithm(
+ ENCRYPTION_ALGORITHM_AES_GCM_16, KEY_LEN_AES_192);
+ aeadBuilder.addEncryptionAlgorithm(
+ ENCRYPTION_ALGORITHM_AES_GCM_12, KEY_LEN_AES_192);
+ aeadBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_GCM_8, KEY_LEN_AES_192);
+ aeadBuilder.addEncryptionAlgorithm(
+ ENCRYPTION_ALGORITHM_AES_GCM_16, KEY_LEN_AES_128);
+ aeadBuilder.addEncryptionAlgorithm(
+ ENCRYPTION_ALGORITHM_AES_GCM_12, KEY_LEN_AES_128);
+ aeadBuilder.addEncryptionAlgorithm(ENCRYPTION_ALGORITHM_AES_GCM_8, KEY_LEN_AES_128);
+ }
proposals.add(aeadBuilder.build());
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 0d06426..a1560b5 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -9903,10 +9903,10 @@
@Override
public void notifyDexLoad(String loadingPackageName, Map<String, String> classLoaderContextMap,
String loaderIsa) {
- if (PLATFORM_PACKAGE_NAME.equals(loadingPackageName)
- && Binder.getCallingUid() != Process.SYSTEM_UID) {
+ int callingUid = Binder.getCallingUid();
+ if (PLATFORM_PACKAGE_NAME.equals(loadingPackageName) && callingUid != Process.SYSTEM_UID) {
Slog.w(TAG, "Non System Server process reporting dex loads as system server. uid="
- + Binder.getCallingUid());
+ + callingUid);
// Do not record dex loads from processes pretending to be system server.
// Only the system server should be assigned the package "android", so reject calls
// that don't satisfy the constraint.
@@ -9917,6 +9917,7 @@
// in order to verify the expectations.
return;
}
+
int userId = UserHandle.getCallingUserId();
ApplicationInfo ai = getApplicationInfo(loadingPackageName, /*flags*/ 0, userId);
if (ai == null) {
@@ -9924,7 +9925,8 @@
+ loadingPackageName + ", user=" + userId);
return;
}
- mDexManager.notifyDexLoad(ai, classLoaderContextMap, loaderIsa, userId);
+ mDexManager.notifyDexLoad(ai, classLoaderContextMap, loaderIsa, userId,
+ Process.isIsolated(callingUid));
}
@Override
diff --git a/services/core/java/com/android/server/pm/dex/DexManager.java b/services/core/java/com/android/server/pm/dex/DexManager.java
index 37f3175..32ba26c 100644
--- a/services/core/java/com/android/server/pm/dex/DexManager.java
+++ b/services/core/java/com/android/server/pm/dex/DexManager.java
@@ -86,6 +86,11 @@
// However it can load verification data - thus we pick the "verify" compiler filter.
private static final String SYSTEM_SERVER_COMPILER_FILTER = "verify";
+ // The suffix we add to the package name when the loading happens in an isolated process.
+ // Note that the double dot creates and "invalid" package name which makes it clear that this
+ // is an artificially constructed name.
+ private static final String ISOLATED_PROCESS_PACKAGE_SUFFIX = "..isolated";
+
private final Context mContext;
// Maps package name to code locations.
@@ -166,12 +171,14 @@
* the class loader context that was used to load them.
* @param loaderIsa the ISA of the app loading the dex files
* @param loaderUserId the user id which runs the code loading the dex files
+ * @param loaderIsIsolatedProcess whether or not the loading process is isolated.
*/
public void notifyDexLoad(ApplicationInfo loadingAppInfo,
- Map<String, String> classLoaderContextMap, String loaderIsa, int loaderUserId) {
+ Map<String, String> classLoaderContextMap, String loaderIsa, int loaderUserId,
+ boolean loaderIsIsolatedProcess) {
try {
notifyDexLoadInternal(loadingAppInfo, classLoaderContextMap, loaderIsa,
- loaderUserId);
+ loaderUserId, loaderIsIsolatedProcess);
} catch (Exception e) {
Slog.w(TAG, "Exception while notifying dex load for package " +
loadingAppInfo.packageName, e);
@@ -181,7 +188,7 @@
@VisibleForTesting
/*package*/ void notifyDexLoadInternal(ApplicationInfo loadingAppInfo,
Map<String, String> classLoaderContextMap, String loaderIsa,
- int loaderUserId) {
+ int loaderUserId, boolean loaderIsIsolatedProcess) {
if (classLoaderContextMap == null) {
return;
}
@@ -195,22 +202,36 @@
return;
}
+ // If this load is coming from an isolated process we need to be able to prevent profile
+ // based optimizations. This is because isolated processes are sandboxed and can only read
+ // world readable files, so they need world readable optimization files. An
+ // example of such a package is webview.
+ //
+ // In order to prevent profile optimization we pretend that the load is coming from a
+ // different package, and so we assign a artificial name to the loading package making it
+ // clear that it comes from an isolated process. This blends well with the entire
+ // usedByOthers logic without needing to special handle isolated process in all dexopt
+ // layers.
+ String loadingPackageAmendedName = loadingAppInfo.packageName;
+ if (loaderIsIsolatedProcess) {
+ loadingPackageAmendedName += ISOLATED_PROCESS_PACKAGE_SUFFIX;
+ }
for (Map.Entry<String, String> mapping : classLoaderContextMap.entrySet()) {
String dexPath = mapping.getKey();
// Find the owning package name.
DexSearchResult searchResult = getDexPackage(loadingAppInfo, dexPath, loaderUserId);
if (DEBUG) {
- Slog.i(TAG, loadingAppInfo.packageName
- + " loads from " + searchResult + " : " + loaderUserId + " : " + dexPath);
+ Slog.i(TAG, loadingPackageAmendedName
+ + " loads from " + searchResult + " : " + loaderUserId + " : " + dexPath);
}
if (searchResult.mOutcome != DEX_SEARCH_NOT_FOUND) {
// TODO(calin): extend isUsedByOtherApps check to detect the cases where
// different apps share the same runtime. In that case we should not mark the dex
// file as isUsedByOtherApps. Currently this is a safe approximation.
- boolean isUsedByOtherApps = !loadingAppInfo.packageName.equals(
- searchResult.mOwningPackageName);
+ boolean isUsedByOtherApps =
+ !loadingPackageAmendedName.equals(searchResult.mOwningPackageName);
boolean primaryOrSplit = searchResult.mOutcome == DEX_SEARCH_FOUND_PRIMARY ||
searchResult.mOutcome == DEX_SEARCH_FOUND_SPLIT;
@@ -249,7 +270,7 @@
// async write to disk to make sure we don't loose the data in case of a reboot.
if (mPackageDexUsage.record(searchResult.mOwningPackageName,
dexPath, loaderUserId, loaderIsa, primaryOrSplit,
- loadingAppInfo.packageName, classLoaderContext, overwriteCLC)) {
+ loadingPackageAmendedName, classLoaderContext, overwriteCLC)) {
mPackageDexUsage.maybeWriteAsync();
}
}
@@ -749,7 +770,7 @@
dexPath, userId, isa, /*primaryOrSplit*/ false,
loadingPackage,
PackageDexUsage.VARIABLE_CLASS_LOADER_CONTEXT,
- /*overwriteCLC*/ false);
+ /*overwriteCLC=*/ false);
update |= newUpdate;
}
if (update) {
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/dex/DexManagerTests.java b/services/tests/mockingservicestests/src/com/android/server/pm/dex/DexManagerTests.java
index 2e0cadf..8abe46f 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/dex/DexManagerTests.java
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/dex/DexManagerTests.java
@@ -410,6 +410,17 @@
}
@Test
+ public void testNotifyUsedByIsolatedProcess() {
+ // Bar loads its own apk but as isolatedProcess.
+ notifyDexLoad(mBarUser0, mBarUser0.getBaseAndSplitDexPaths(), mUser0,
+ /*isolatedProcess=*/ true);
+
+ // Bar is used by an isolated process and should be marked as usedByOtherApps
+ PackageUseInfo pui = getPackageUseInfo(mBarUser0);
+ assertIsUsedByOtherApps(mBarUser0, pui, true);
+ }
+
+ @Test
public void testNotifyPackageUpdatedCodeLocations() {
// Simulate a split update.
String newSplit = mBarUser0.replaceLastSplit();
@@ -545,7 +556,7 @@
List<String> classLoaders =
Arrays.asList(PATH_CLASS_LOADER_NAME, UNSUPPORTED_CLASS_LOADER_NAME);
List<String> classPaths = Arrays.asList(classPath, classPath);
- notifyDexLoad(mBarUser0, classLoaders, classPaths, mUser0);
+ notifyDexLoad(mBarUser0, classLoaders, classPaths, mUser0, /*isolatedProcess=*/ false);
assertNoUseInfo(mBarUser0);
@@ -664,7 +675,8 @@
expectedContexts[i] += contextSuffix;
}
- notifyDexLoad(mFooUser0, fooSecondaries, expectedContexts, mUser0);
+ notifyDexLoad(mFooUser0, fooSecondaries, expectedContexts, mUser0,
+ /*isolatedProcess=*/ false);
PackageUseInfo pui = getPackageUseInfo(mFooUser0);
assertIsUsedByOtherApps(mFooUser0, pui, false);
@@ -838,26 +850,32 @@
assertEquals(codePath, isUsedByOtherApps, pui.isUsedByOtherApps(codePath));
}
}
+
private void notifyDexLoad(TestData testData, List<String> dexPaths, int loaderUserId) {
+ notifyDexLoad(testData, dexPaths, loaderUserId, /*isolatedProcess=*/ false);
+ }
+
+ private void notifyDexLoad(TestData testData, List<String> dexPaths, int loaderUserId,
+ boolean isolatedProcess) {
// By default, assume a single class loader in the chain.
// This makes writing tests much easier.
List<String> classLoaders = Arrays.asList(testData.mClassLoader);
List<String> classPaths = dexPaths != null
? Arrays.<String>asList(String.join(File.pathSeparator, dexPaths)) : null;
- notifyDexLoad(testData, classLoaders, classPaths, loaderUserId);
+ notifyDexLoad(testData, classLoaders, classPaths, loaderUserId, isolatedProcess);
}
private void notifyDexLoad(TestData testData, List<String> classLoaders,
- List<String> classPaths, int loaderUserId) {
+ List<String> classPaths, int loaderUserId, boolean isolatedProcess) {
String[] classLoaderContexts = computeClassLoaderContexts(classLoaders, classPaths);
// We call the internal function so any exceptions thrown cause test failures.
List<String> dexPaths = classPaths != null
? Arrays.asList(classPaths.get(0).split(File.pathSeparator)) : Arrays.asList();
- notifyDexLoad(testData, dexPaths, classLoaderContexts, loaderUserId);
+ notifyDexLoad(testData, dexPaths, classLoaderContexts, loaderUserId, isolatedProcess);
}
private void notifyDexLoad(TestData testData, List<String> dexPaths,
- String[] classLoaderContexts, int loaderUserId) {
+ String[] classLoaderContexts, int loaderUserId, boolean isolatedProcess) {
assertTrue(dexPaths.size() == classLoaderContexts.length);
HashMap<String, String> dexPathMapping = new HashMap<>(dexPaths.size());
for (int i = 0; i < dexPaths.size(); i++) {
@@ -865,7 +883,7 @@
? classLoaderContexts[i] : PackageDexUsage.UNSUPPORTED_CLASS_LOADER_CONTEXT);
}
mDexManager.notifyDexLoadInternal(testData.mPackageInfo.applicationInfo, dexPathMapping,
- testData.mLoaderIsa, loaderUserId);
+ testData.mLoaderIsa, loaderUserId, isolatedProcess);
}
private String[] computeClassLoaderContexts(List<String> classLoaders,