diff options
31 files changed, 887 insertions, 140 deletions
diff --git a/Android.bp b/Android.bp index c7ab00f99d08..c5d557c04472 100644 --- a/Android.bp +++ b/Android.bp @@ -497,7 +497,7 @@ stubs_defaults { "android.hardware.usb.gadget-V1.0-java", "android.hardware.vibrator-V1.3-java", "framework-protos", - "stable.core.platform.api.stubs", + "art.module.public.api", // There are a few classes from modules used by the core that // need to be resolved by metalava. We use a prebuilt stub of the // full sdk to ensure we can resolve them. If a new class gets added, diff --git a/core/api/system-current.txt b/core/api/system-current.txt index ea626b3c70ef..bb42ddcda37f 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -12331,6 +12331,7 @@ package android.telephony.ims.stub { field public static final int REGISTRATION_TECH_IWLAN = 1; // 0x1 field public static final int REGISTRATION_TECH_LTE = 0; // 0x0 field public static final int REGISTRATION_TECH_NONE = -1; // 0xffffffff + field public static final int REGISTRATION_TECH_NR = 3; // 0x3 } public class ImsSmsImplBase { diff --git a/core/java/android/bluetooth/BluetoothDevice.java b/core/java/android/bluetooth/BluetoothDevice.java index a40bf343239e..11b45e32c425 100644 --- a/core/java/android/bluetooth/BluetoothDevice.java +++ b/core/java/android/bluetooth/BluetoothDevice.java @@ -1605,13 +1605,13 @@ public final class BluetoothDevice implements Parcelable { * * <p>This API is asynchronous and {@link #ACTION_UUID} intent is sent, * with the UUIDs supported by the remote end. If there is an error - * in getting the SDP records or if the process takes a long time, - * {@link #ACTION_UUID} intent is sent with the UUIDs that is currently - * present in the cache. Clients should use the {@link #getUuids} to get UUIDs + * in getting the SDP records or if the process takes a long time, or the device is bonding and + * we have its UUIDs cached, {@link #ACTION_UUID} intent is sent with the UUIDs that is + * currently present in the cache. Clients should use the {@link #getUuids} to get UUIDs * if service discovery is not to be performed. * * @return False if the check fails, True if the process of initiating an ACL connection - * to the remote device was started. + * to the remote device was started or cached UUIDs will be broadcast. */ @RequiresPermission(Manifest.permission.BLUETOOTH) public boolean fetchUuidsWithSdp() { diff --git a/core/java/android/net/IpSecManager.java b/core/java/android/net/IpSecManager.java index 98acd98cc465..01d1aa533a8f 100644 --- a/core/java/android/net/IpSecManager.java +++ b/core/java/android/net/IpSecManager.java @@ -79,6 +79,16 @@ public final class IpSecManager { public static final int DIRECTION_OUT = 1; /** + * Used when applying a transform to direct traffic through an {@link IpSecTransform} for + * forwarding between interfaces. + * + * <p>See {@link #applyTransportModeTransform(Socket, int, IpSecTransform)}. + * + * @hide + */ + public static final int DIRECTION_FWD = 2; + + /** * The Security Parameter Index (SPI) 0 indicates an unknown or invalid index. * * <p>No IPsec packet may contain an SPI of 0. diff --git a/core/java/android/net/vcn/VcnNetworkPolicyResult.java b/core/java/android/net/vcn/VcnNetworkPolicyResult.java index 5e938200639c..14e70cfeb18a 100644 --- a/core/java/android/net/vcn/VcnNetworkPolicyResult.java +++ b/core/java/android/net/vcn/VcnNetworkPolicyResult.java @@ -87,6 +87,16 @@ public final class VcnNetworkPolicyResult implements Parcelable { && mNetworkCapabilities.equals(that.mNetworkCapabilities); } + @Override + public String toString() { + return "VcnNetworkPolicyResult { " + + "mIsTeardownRequested = " + + mIsTearDownRequested + + ", mNetworkCapabilities" + + mNetworkCapabilities + + " }"; + } + /** {@inheritDoc} */ @Override public int describeContents() { diff --git a/core/java/android/net/vcn/VcnUnderlyingNetworkPolicy.java b/core/java/android/net/vcn/VcnUnderlyingNetworkPolicy.java index b47d5642419e..b0d4f3be248f 100644 --- a/core/java/android/net/vcn/VcnUnderlyingNetworkPolicy.java +++ b/core/java/android/net/vcn/VcnUnderlyingNetworkPolicy.java @@ -85,6 +85,11 @@ public final class VcnUnderlyingNetworkPolicy implements Parcelable { return mVcnNetworkPolicyResult.equals(that.mVcnNetworkPolicyResult); } + @Override + public String toString() { + return mVcnNetworkPolicyResult.toString(); + } + /** {@inheritDoc} */ @Override public int describeContents() { diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java index e47ffcc4ff4c..5017d9e8d905 100755 --- a/core/java/android/os/Build.java +++ b/core/java/android/os/Build.java @@ -424,7 +424,8 @@ public class Build { * Magic version number for a current development build, which has * not yet turned into an official release. */ - public static final int CUR_DEVELOPMENT = VMRuntime.SDK_VERSION_CUR_DEVELOPMENT; + // This must match VMRuntime.SDK_VERSION_CUR_DEVELOPMENT. + public static final int CUR_DEVELOPMENT = 10000; /** * October 2008: The original, first, version of Android. Yay! @@ -1311,6 +1312,7 @@ public class Build { * selinux into "permissive" mode in particular. * @hide */ + @UnsupportedAppUsage public static final boolean IS_DEBUGGABLE = SystemProperties.getInt("ro.debuggable", 0) == 1; diff --git a/core/java/android/os/Debug.java b/core/java/android/os/Debug.java index 1e60f74dd3d3..a7516a428ff0 100644 --- a/core/java/android/os/Debug.java +++ b/core/java/android/os/Debug.java @@ -74,8 +74,9 @@ public final class Debug * * @deprecated Accurate counting is a burden on the runtime and may be removed. */ + // This must match VMDebug.TRACE_COUNT_ALLOCS. @Deprecated - public static final int TRACE_COUNT_ALLOCS = VMDebug.TRACE_COUNT_ALLOCS; + public static final int TRACE_COUNT_ALLOCS = 1; /** * Flags for printLoadedClasses(). Default behavior is to only show diff --git a/core/java/com/android/internal/os/BinderCallsStats.java b/core/java/com/android/internal/os/BinderCallsStats.java index 0fb2728aed73..dbba469dda1a 100644 --- a/core/java/com/android/internal/os/BinderCallsStats.java +++ b/core/java/com/android/internal/os/BinderCallsStats.java @@ -54,6 +54,7 @@ public class BinderCallsStats implements BinderInternal.Observer { public static final int PERIODIC_SAMPLING_INTERVAL_DEFAULT = 1000; public static final boolean DEFAULT_TRACK_SCREEN_INTERACTIVE = false; public static final boolean DEFAULT_TRACK_DIRECT_CALLING_UID = true; + public static final boolean DEFAULT_COLLECT_LATENCY_DATA = false; public static final int MAX_BINDER_CALL_STATS_COUNT_DEFAULT = 1500; private static final String DEBUG_ENTRY_PREFIX = "__DEBUG_"; @@ -89,19 +90,27 @@ public class BinderCallsStats implements BinderInternal.Observer { private boolean mAddDebugEntries = false; private boolean mTrackDirectCallingUid = DEFAULT_TRACK_DIRECT_CALLING_UID; private boolean mTrackScreenInteractive = DEFAULT_TRACK_SCREEN_INTERACTIVE; + private boolean mCollectLatencyData = DEFAULT_COLLECT_LATENCY_DATA; private CachedDeviceState.Readonly mDeviceState; private CachedDeviceState.TimeInStateStopwatch mBatteryStopwatch; + private BinderLatencyObserver mLatencyObserver; + /** Injector for {@link BinderCallsStats}. */ public static class Injector { public Random getRandomGenerator() { return new Random(); } + + public BinderLatencyObserver getLatencyObserver() { + return new BinderLatencyObserver(new BinderLatencyObserver.Injector()); + } } public BinderCallsStats(Injector injector) { this.mRandom = injector.getRandomGenerator(); + this.mLatencyObserver = injector.getLatencyObserver(); } public void setDeviceState(@NonNull CachedDeviceState.Readonly deviceState) { @@ -128,7 +137,10 @@ public class BinderCallsStats implements BinderInternal.Observer { if (shouldRecordDetailedData()) { s.cpuTimeStarted = getThreadTimeMicro(); s.timeStarted = getElapsedRealtimeMicro(); + } else if (mCollectLatencyData) { + s.timeStarted = getElapsedRealtimeMicro(); } + return s; } @@ -153,6 +165,10 @@ public class BinderCallsStats implements BinderInternal.Observer { private void processCallEnded(CallSession s, int parcelRequestSize, int parcelReplySize, int workSourceUid) { + if (mCollectLatencyData) { + mLatencyObserver.callEnded(s); + } + // Non-negative time signals we need to record data for this call. final boolean recordCall = s.cpuTimeStarted >= 0; final long duration; @@ -555,6 +571,17 @@ public class BinderCallsStats implements BinderInternal.Observer { } } + /** Whether to collect latency histograms. */ + public void setCollectLatencyData(boolean collectLatencyData) { + mCollectLatencyData = collectLatencyData; + } + + /** Whether to collect latency histograms. */ + @VisibleForTesting + public boolean getCollectLatencyData() { + return mCollectLatencyData; + } + public void reset() { synchronized (mLock) { mCallStatsCount = 0; @@ -565,6 +592,8 @@ public class BinderCallsStats implements BinderInternal.Observer { if (mBatteryStopwatch != null) { mBatteryStopwatch.reset(); } + // Do not reset the latency observer as binder stats and latency will be pushed to + // statsd at different intervals so the resets should not be coupled. } } @@ -767,6 +796,10 @@ public class BinderCallsStats implements BinderInternal.Observer { return mExceptionCounts; } + public BinderLatencyObserver getLatencyObserver() { + return mLatencyObserver; + } + @VisibleForTesting public static <T> List<T> getHighestValues(List<T> list, ToDoubleFunction<T> toDouble, double percentile) { diff --git a/core/java/com/android/internal/os/BinderLatencyObserver.java b/core/java/com/android/internal/os/BinderLatencyObserver.java new file mode 100644 index 000000000000..92b495284de2 --- /dev/null +++ b/core/java/com/android/internal/os/BinderLatencyObserver.java @@ -0,0 +1,157 @@ +/* + * Copyright (C) 2017 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.Nullable; +import android.os.Binder; +import android.os.SystemClock; +import android.util.ArrayMap; +import android.util.Slog; + +import com.android.internal.annotations.GuardedBy; +import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.os.BinderInternal.CallSession; + +import java.util.ArrayList; +import java.util.Random; + +/** Collects statistics about Binder call latency per calling API and method. */ +public class BinderLatencyObserver { + private static final String TAG = "BinderLatencyObserver"; + public static final int PERIODIC_SAMPLING_INTERVAL_DEFAULT = 10; + + // This is not the final data structure - we will eventually store latency histograms instead of + // raw samples as it is much more memory / disk space efficient. + // TODO(b/179999191): change this to store the histogram. + // TODO(b/179999191): pre allocate the array size so we would not have to resize this. + @GuardedBy("mLock") + private final ArrayMap<LatencyDims, ArrayList<Long>> mLatencySamples = new ArrayMap<>(); + private final Object mLock = new Object(); + + // Sampling period to control how often to track CPU usage. 1 means all calls, 100 means ~1 out + // of 100 requests. + private int mPeriodicSamplingInterval = PERIODIC_SAMPLING_INTERVAL_DEFAULT; + private final Random mRandom; + + /** Injector for {@link BinderLatencyObserver}. */ + public static class Injector { + public Random getRandomGenerator() { + return new Random(); + } + } + + public BinderLatencyObserver(Injector injector) { + mRandom = injector.getRandomGenerator(); + } + + /** Should be called when a Binder call completes, will store latency data. */ + public void callEnded(@Nullable CallSession s) { + if (s == null || s.exceptionThrown || !shouldKeepSample()) { + return; + } + + LatencyDims dims = new LatencyDims(s.binderClass, s.transactionCode); + long callDuration = getElapsedRealtimeMicro() - s.timeStarted; + + synchronized (mLock) { + if (!mLatencySamples.containsKey(dims)) { + mLatencySamples.put(dims, new ArrayList<Long>()); + } + + mLatencySamples.get(dims).add(callDuration); + } + } + + protected long getElapsedRealtimeMicro() { + return SystemClock.elapsedRealtimeNanos() / 1000; + } + + protected boolean shouldKeepSample() { + return mRandom.nextInt() % mPeriodicSamplingInterval == 0; + } + + /** Updates the sampling interval. */ + public void setSamplingInterval(int samplingInterval) { + if (samplingInterval <= 0) { + Slog.w(TAG, "Ignored invalid sampling interval (value must be positive): " + + samplingInterval); + return; + } + + synchronized (mLock) { + if (samplingInterval != mPeriodicSamplingInterval) { + mPeriodicSamplingInterval = samplingInterval; + reset(); + } + } + } + + /** Resets the sample collection. */ + public void reset() { + synchronized (mLock) { + mLatencySamples.clear(); + } + } + + /** Container for binder latency information. */ + public static class LatencyDims { + // Binder interface descriptor. + private Class<? extends Binder> mBinderClass; + // Binder transaction code. + private int mTransactionCode; + // Cached hash code, 0 if not set yet. + private int mHashCode = 0; + + public LatencyDims(Class<? extends Binder> binderClass, int transactionCode) { + this.mBinderClass = binderClass; + this.mTransactionCode = transactionCode; + } + + public Class<? extends Binder> getBinderClass() { + return mBinderClass; + } + + public int getTransactionCode() { + return mTransactionCode; + } + + @Override + public boolean equals(final Object other) { + if (other == null || !(other instanceof LatencyDims)) { + return false; + } + LatencyDims o = (LatencyDims) other; + return mTransactionCode == o.getTransactionCode() && mBinderClass == o.getBinderClass(); + } + + @Override + public int hashCode() { + if (mHashCode != 0) { + return mHashCode; + } + int hash = mTransactionCode; + hash = 31 * hash + mBinderClass.hashCode(); + mHashCode = hash; + return hash; + } + } + + @VisibleForTesting + public ArrayMap<LatencyDims, ArrayList<Long>> getLatencySamples() { + return mLatencySamples; + } +} diff --git a/core/jni/android_media_AudioSystem.cpp b/core/jni/android_media_AudioSystem.cpp index feb8930a0dbf..1a7f6e8863ff 100644 --- a/core/jni/android_media_AudioSystem.cpp +++ b/core/jni/android_media_AudioSystem.cpp @@ -2273,10 +2273,19 @@ android_media_AudioSystem_setSurroundFormatEnabled(JNIEnv *env, jobject thiz, return (jint)nativeToJavaStatus(status); } -static jint android_media_AudioSystem_get_FCC_8(JNIEnv *env, jobject thiz) { +static jint android_media_AudioSystem_getMaxChannelCount(JNIEnv *env, jobject thiz) { return FCC_8; } +static jint android_media_AudioSystem_getMaxSampleRate(JNIEnv *env, jobject thiz) { + // see frameworks/av/services/audiopolicy/common/include/policy.h + return 192000; // SAMPLE_RATE_HZ_MAX (for API) +} + +static jint android_media_AudioSystem_getMinSampleRate(JNIEnv *env, jobject thiz) { + return 4000; // SAMPLE_RATE_HZ_MIN (for API) +} + static jint android_media_AudioSystem_setAssistantUid(JNIEnv *env, jobject thiz, jint uid) { @@ -2672,14 +2681,18 @@ static const JNINativeMethod gEventHandlerMethods[] = { (void *)android_media_AudioSystem_eventHandlerFinalize}, }; -static const JNINativeMethod gGetFCC8Methods[] = { - {"native_get_FCC_8", "()I", (void *)android_media_AudioSystem_get_FCC_8}, +static const JNINativeMethod gFrameworkCapabilities[] = { + {"native_getMaxChannelCount", "()I", (void *)android_media_AudioSystem_getMaxChannelCount}, + {"native_getMaxSampleRate", "()I", (void *)android_media_AudioSystem_getMaxSampleRate}, + {"native_getMinSampleRate", "()I", (void *)android_media_AudioSystem_getMinSampleRate}, }; int register_android_media_AudioSystem(JNIEnv *env) { // This needs to be done before hooking up methods AudioTrackRoutingProxy (below) - RegisterMethodsOrDie(env, kClassPathName, gGetFCC8Methods, NELEM(gGetFCC8Methods)); + // as the calls are performed in the static initializer of AudioSystem. + RegisterMethodsOrDie(env, kClassPathName, gFrameworkCapabilities, + NELEM(gFrameworkCapabilities)); jclass arrayListClass = FindClassOrDie(env, "java/util/ArrayList"); gArrayListClass = MakeGlobalRefOrDie(env, arrayListClass); diff --git a/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java b/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java index eb9c7b66d642..1156120217aa 100644 --- a/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java +++ b/core/tests/coretests/src/com/android/internal/os/BinderCallsStatsTest.java @@ -753,6 +753,34 @@ public class BinderCallsStatsTest { assertEquals(1, callStats.recordedCallCount); } + @Test + public void testLatencyCollectionEnabled() { + TestBinderCallsStats bcs = new TestBinderCallsStats(); + bcs.setCollectLatencyData(true); + + Binder binder = new Binder(); + CallSession callSession = bcs.callStarted(binder, 1, WORKSOURCE_UID); + bcs.time += 10; + bcs.elapsedTime += 20; + bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE, WORKSOURCE_UID); + + assertEquals(1, bcs.getLatencyObserver().getLatencySamples().size()); + } + + @Test + public void testLatencyCollectionDisabledByDefault() { + TestBinderCallsStats bcs = new TestBinderCallsStats(); + assertEquals(false, bcs.getCollectLatencyData()); + + Binder binder = new Binder(); + CallSession callSession = bcs.callStarted(binder, 1, WORKSOURCE_UID); + bcs.time += 10; + bcs.elapsedTime += 20; + bcs.callEnded(callSession, REQUEST_SIZE, REPLY_SIZE, WORKSOURCE_UID); + + assertEquals(0, bcs.getLatencyObserver().getLatencySamples().size()); + } + class TestBinderCallsStats extends BinderCallsStats { public int callingUid = CALLING_UID; public long time = 1234; @@ -774,6 +802,10 @@ public class BinderCallsStatsTest { } }; } + + public BinderLatencyObserver getLatencyObserver() { + return new BinderLatencyObserverTest.TestBinderLatencyObserver(); + } }); setSamplingInterval(1); setAddDebugEntries(false); diff --git a/core/tests/coretests/src/com/android/internal/os/BinderLatencyObserverTest.java b/core/tests/coretests/src/com/android/internal/os/BinderLatencyObserverTest.java new file mode 100644 index 000000000000..36915a205ee0 --- /dev/null +++ b/core/tests/coretests/src/com/android/internal/os/BinderLatencyObserverTest.java @@ -0,0 +1,110 @@ +/* + * Copyright (C) 2018 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 static com.google.common.truth.Truth.assertThat; + +import static org.junit.Assert.assertEquals; + +import android.os.Binder; +import android.platform.test.annotations.Presubmit; +import android.util.ArrayMap; + +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 org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Random; + +@SmallTest +@RunWith(AndroidJUnit4.class) +@Presubmit +public class BinderLatencyObserverTest { + @Test + public void testLatencyCollectionWithMultipleClasses() { + TestBinderLatencyObserver blo = new TestBinderLatencyObserver(); + + Binder binder = new Binder(); + CallSession callSession = new CallSession(); + callSession.binderClass = binder.getClass(); + callSession.transactionCode = 1; + blo.callEnded(callSession); + blo.callEnded(callSession); + callSession.transactionCode = 2; + blo.callEnded(callSession); + + ArrayMap<LatencyDims, ArrayList<Long>> latencySamples = blo.getLatencySamples(); + assertEquals(2, latencySamples.keySet().size()); + assertThat(latencySamples.get(new LatencyDims(binder.getClass(), 1))) + .containsExactlyElementsIn(Arrays.asList(1L, 2L)); + assertThat(latencySamples.get(new LatencyDims(binder.getClass(), 2))).containsExactly(3L); + } + + @Test + public void testSampling() { + TestBinderLatencyObserver blo = new TestBinderLatencyObserver(); + blo.setSamplingInterval(2); + + Binder binder = new Binder(); + CallSession callSession = new CallSession(); + callSession.binderClass = binder.getClass(); + callSession.transactionCode = 1; + blo.callEnded(callSession); + callSession.transactionCode = 2; + blo.callEnded(callSession); + + ArrayMap<LatencyDims, ArrayList<Long>> latencySamples = blo.getLatencySamples(); + assertEquals(1, latencySamples.size()); + LatencyDims dims = latencySamples.keySet().iterator().next(); + assertEquals(binder.getClass(), dims.getBinderClass()); + assertEquals(1, dims.getTransactionCode()); + ArrayList<Long> values = latencySamples.get(dims); + assertThat(values).containsExactly(1L); + } + + public static class TestBinderLatencyObserver extends BinderLatencyObserver { + private long mElapsedTimeCallCount = 0; + + TestBinderLatencyObserver() { + // Make random generator not random. + super(new Injector() { + public Random getRandomGenerator() { + return new Random() { + int mCallCount = 0; + + public int nextInt() { + return mCallCount++; + } + }; + } + }); + setSamplingInterval(1); + } + + @Override + protected long getElapsedRealtimeMicro() { + return ++mElapsedTimeCallCount; + } + } +} diff --git a/media/java/android/media/AudioFormat.java b/media/java/android/media/AudioFormat.java index bd2524f4b4b1..5885c7dd8b50 100644 --- a/media/java/android/media/AudioFormat.java +++ b/media/java/android/media/AudioFormat.java @@ -518,13 +518,13 @@ public final class AudioFormat implements Parcelable { * @hide */ // never unhide - public static final int SAMPLE_RATE_HZ_MIN = 4000; + public static final int SAMPLE_RATE_HZ_MIN = AudioSystem.SAMPLE_RATE_HZ_MIN; /** Maximum value for sample rate, * assuming AudioTrack and AudioRecord share the same limitations. * @hide */ // never unhide - public static final int SAMPLE_RATE_HZ_MAX = 192000; + public static final int SAMPLE_RATE_HZ_MAX = AudioSystem.SAMPLE_RATE_HZ_MAX; /** Sample rate will be a route-dependent value. * For AudioTrack, it is usually the sink sample rate, * and for AudioRecord it is usually the source sample rate. diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java index 8425185b90d0..2d9bdafa1f45 100644 --- a/media/java/android/media/AudioSystem.java +++ b/media/java/android/media/AudioSystem.java @@ -101,11 +101,31 @@ public class AudioSystem */ public static final int NUM_STREAMS = 5; + /* + * Framework static final constants that are primitives or Strings + * accessed by CTS tests or internal applications must be set from methods + * (or in a static block) to prevent Java compile-time replacement. + * We set them from methods so they are read from the device framework. + * Do not un-hide or change to a numeric literal. + */ + /** Maximum value for AudioTrack channel count - * @hide public for MediaCode only, do not un-hide or change to a numeric literal + * @hide + */ + public static final int OUT_CHANNEL_COUNT_MAX = native_getMaxChannelCount(); + private static native int native_getMaxChannelCount(); + + /** Maximum value for sample rate, used by AudioFormat. + * @hide + */ + public static final int SAMPLE_RATE_HZ_MAX = native_getMaxSampleRate(); + private static native int native_getMaxSampleRate(); + + /** Minimum value for sample rate, used by AudioFormat. + * @hide */ - public static final int OUT_CHANNEL_COUNT_MAX = native_get_FCC_8(); - private static native int native_get_FCC_8(); + public static final int SAMPLE_RATE_HZ_MIN = native_getMinSampleRate(); + private static native int native_getMinSampleRate(); // Expose only the getter method publicly so we can change it in the future private static final int NUM_STREAM_TYPES = 12; diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/AccessPointTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/AccessPointTest.java index bcabec858487..2c1d3e2af118 100644 --- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/AccessPointTest.java +++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/AccessPointTest.java @@ -683,6 +683,16 @@ public class AccessPointTest { assertThat(ap.getTitle()).isEqualTo(providerFriendlyName); } + // This method doesn't copy mIsFailover, mIsAvailable and mIsRoaming because NetworkInfo + // doesn't expose those three set methods. But that's fine since the tests don't use those three + // variables. + private NetworkInfo copyNetworkInfo(NetworkInfo ni) { + final NetworkInfo copy = new NetworkInfo(ni.getType(), ni.getSubtype(), ni.getTypeName(), + ni.getSubtypeName()); + copy.setDetailedState(ni.getDetailedState(), ni.getReason(), ni.getExtraInfo()); + return copy; + } + @Test public void testUpdateNetworkInfo_returnsTrue() { int networkId = 123; @@ -704,7 +714,7 @@ public class AccessPointTest { .setWifiInfo(wifiInfo) .build(); - NetworkInfo newInfo = new NetworkInfo(networkInfo); + NetworkInfo newInfo = copyNetworkInfo(networkInfo); newInfo.setDetailedState(NetworkInfo.DetailedState.CONNECTED, "", ""); assertThat(ap.update(config, wifiInfo, newInfo)).isTrue(); } @@ -730,7 +740,7 @@ public class AccessPointTest { .setWifiInfo(wifiInfo) .build(); - NetworkInfo newInfo = new NetworkInfo(networkInfo); // same values + NetworkInfo newInfo = copyNetworkInfo(networkInfo); // same values assertThat(ap.update(config, wifiInfo, newInfo)).isFalse(); } @@ -755,7 +765,7 @@ public class AccessPointTest { .setWifiInfo(wifiInfo) .build(); - NetworkInfo newInfo = new NetworkInfo(networkInfo); // same values + NetworkInfo newInfo = copyNetworkInfo(networkInfo); // same values wifiInfo.setRssi(rssi + 1); assertThat(ap.update(config, wifiInfo, newInfo)).isTrue(); } @@ -781,7 +791,7 @@ public class AccessPointTest { .setWifiInfo(wifiInfo) .build(); - NetworkInfo newInfo = new NetworkInfo(networkInfo); // same values + NetworkInfo newInfo = copyNetworkInfo(networkInfo); // same values wifiInfo.setRssi(WifiInfo.INVALID_RSSI); assertThat(ap.update(config, wifiInfo, newInfo)).isFalse(); } diff --git a/services/core/java/com/android/server/BinderCallsStatsService.java b/services/core/java/com/android/server/BinderCallsStatsService.java index c771eb701b1d..9c8582f78134 100644 --- a/services/core/java/com/android/server/BinderCallsStatsService.java +++ b/services/core/java/com/android/server/BinderCallsStatsService.java @@ -40,6 +40,7 @@ import com.android.internal.os.AppIdToPackageMap; import com.android.internal.os.BackgroundThread; import com.android.internal.os.BinderCallsStats; import com.android.internal.os.BinderInternal; +import com.android.internal.os.BinderLatencyObserver; import com.android.internal.os.CachedDeviceState; import com.android.internal.util.DumpUtils; @@ -120,6 +121,7 @@ public class BinderCallsStatsService extends Binder { /** Listens for flag changes. */ private static class SettingsObserver extends ContentObserver { + // Settings for BinderCallsStats. private static final String SETTINGS_ENABLED_KEY = "enabled"; private static final String SETTINGS_DETAILED_TRACKING_KEY = "detailed_tracking"; private static final String SETTINGS_UPLOAD_DATA_KEY = "upload_data"; @@ -127,6 +129,10 @@ public class BinderCallsStatsService extends Binder { private static final String SETTINGS_TRACK_SCREEN_INTERACTIVE_KEY = "track_screen_state"; private static final String SETTINGS_TRACK_DIRECT_CALLING_UID_KEY = "track_calling_uid"; private static final String SETTINGS_MAX_CALL_STATS_KEY = "max_call_stats_count"; + // Settings for BinderLatencyObserver. + 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 boolean mEnabled; private final Uri mUri = Settings.Global.getUriFor(Settings.Global.BINDER_CALLS_STATS); @@ -180,6 +186,13 @@ public class BinderCallsStatsService extends Binder { mBinderCallsStats.setTrackDirectCallerUid( mParser.getBoolean(SETTINGS_TRACK_DIRECT_CALLING_UID_KEY, BinderCallsStats.DEFAULT_TRACK_DIRECT_CALLING_UID)); + mBinderCallsStats.setCollectLatencyData( + mParser.getBoolean(SETTINGS_COLLECT_LATENCY_DATA_KEY, + BinderCallsStats.DEFAULT_COLLECT_LATENCY_DATA)); + // Binder latency observer settings. + mBinderCallsStats.getLatencyObserver().setSamplingInterval(mParser.getInt( + SETTINGS_LATENCY_OBSERVER_SAMPLING_INTERVAL_KEY, + BinderLatencyObserver.PERIODIC_SAMPLING_INTERVAL_DEFAULT)); final boolean enabled = @@ -198,6 +211,7 @@ public class BinderCallsStatsService extends Binder { mEnabled = enabled; mBinderCallsStats.reset(); mBinderCallsStats.setAddDebugEntries(enabled); + mBinderCallsStats.getLatencyObserver().reset(); } } } diff --git a/services/core/java/com/android/server/IpSecService.java b/services/core/java/com/android/server/IpSecService.java index 794cb9301d69..d574e74d398f 100644 --- a/services/core/java/com/android/server/IpSecService.java +++ b/services/core/java/com/android/server/IpSecService.java @@ -49,6 +49,7 @@ import android.net.util.NetdService; import android.os.Binder; import android.os.IBinder; import android.os.ParcelFileDescriptor; +import android.os.Process; import android.os.RemoteException; import android.os.ServiceSpecificException; import android.system.ErrnoException; @@ -65,6 +66,7 @@ import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.Preconditions; import com.android.net.module.util.NetdUtils; +import com.android.net.module.util.PermissionUtils; import libcore.io.IoUtils; @@ -466,8 +468,7 @@ public class IpSecService extends IIpSecService.Stub { /** Safety method; guards against access of other user's UserRecords */ private void checkCallerUid(int uid) { - if (uid != Binder.getCallingUid() - && android.os.Process.SYSTEM_UID != Binder.getCallingUid()) { + if (uid != Binder.getCallingUid() && Process.SYSTEM_UID != Binder.getCallingUid()) { throw new SecurityException("Attempted access of unowned resources"); } } @@ -1105,11 +1106,15 @@ public class IpSecService extends IIpSecService.Stub { * Checks the user-provided direction field and throws an IllegalArgumentException if it is not * DIRECTION_IN or DIRECTION_OUT */ - private static void checkDirection(int direction) { + private void checkDirection(int direction) { switch (direction) { case IpSecManager.DIRECTION_OUT: case IpSecManager.DIRECTION_IN: return; + case IpSecManager.DIRECTION_FWD: + // Only NETWORK_STACK or PERMISSION_NETWORK_STACK allowed to use forward policies + PermissionUtils.enforceNetworkStackPermission(mContext); + return; } throw new IllegalArgumentException("Invalid Direction: " + direction); } @@ -1353,6 +1358,16 @@ public class IpSecService extends IIpSecService.Stub { ikey, 0xffffffff, resourceId); + netd.ipSecAddSecurityPolicy( + callerUid, + selAddrFamily, + IpSecManager.DIRECTION_FWD, + remoteAddr, + localAddr, + 0, + ikey, + 0xffffffff, + resourceId); } userRecord.mTunnelInterfaceRecords.put( @@ -1820,7 +1835,7 @@ public class IpSecService extends IIpSecService.Stub { int mark = (direction == IpSecManager.DIRECTION_OUT) ? tunnelInterfaceInfo.getOkey() - : tunnelInterfaceInfo.getIkey(); + : tunnelInterfaceInfo.getIkey(); // Ikey also used for FWD policies try { // Default to using the invalid SPI of 0 for inbound SAs. This allows policies to skip diff --git a/services/core/java/com/android/server/OWNERS b/services/core/java/com/android/server/OWNERS index 55e1b46a51a9..4ab947d93f70 100644 --- a/services/core/java/com/android/server/OWNERS +++ b/services/core/java/com/android/server/OWNERS @@ -32,8 +32,9 @@ per-file GestureLauncherService.java = file:platform/packages/apps/EmergencyInfo per-file IpSecService.java = file:/services/core/java/com/android/server/net/OWNERS per-file MmsServiceBroker.java = file:/telephony/OWNERS per-file NetIdManager.java = file:/services/core/java/com/android/server/net/OWNERS -per-file PackageWatchdog.java = file:/services/core/java/com/android/server/rollback/OWNERS +per-file PackageWatchdog.java, RescueParty.java = file:/services/core/java/com/android/server/rollback/OWNERS per-file PinnerService.java = file:/apct-tests/perftests/OWNERS +per-file RescueParty.java = fdunlap@google.com, shuc@google.com per-file TelephonyRegistry.java = file:/telephony/OWNERS per-file UiModeManagerService.java = file:/packages/SystemUI/OWNERS per-file VcnManagementService.java = file:/services/core/java/com/android/server/vcn/OWNERS diff --git a/services/core/java/com/android/server/VcnManagementService.java b/services/core/java/com/android/server/VcnManagementService.java index 471d2af2784f..1fd637ad20c4 100644 --- a/services/core/java/com/android/server/VcnManagementService.java +++ b/services/core/java/com/android/server/VcnManagementService.java @@ -434,6 +434,7 @@ public class VcnManagementService extends IVcnManagementService.Stub { synchronized (mLock) { final TelephonySubscriptionSnapshot oldSnapshot = mLastSnapshot; mLastSnapshot = snapshot; + Slog.d(TAG, "new snapshot: " + mLastSnapshot); // Start any VCN instances as necessary for (Entry<ParcelUuid, VcnConfig> entry : mConfigs.entrySet()) { @@ -536,7 +537,7 @@ public class VcnManagementService extends IVcnManagementService.Stub { @GuardedBy("mLock") private void startVcnLocked(@NonNull ParcelUuid subscriptionGroup, @NonNull VcnConfig config) { - Slog.v(TAG, "Starting VCN config for subGrp: " + subscriptionGroup); + Slog.d(TAG, "Starting VCN config for subGrp: " + subscriptionGroup); // TODO(b/176939047): Support multiple VCNs active at the same time, or limit to one active // VCN. @@ -558,7 +559,7 @@ public class VcnManagementService extends IVcnManagementService.Stub { @GuardedBy("mLock") private void startOrUpdateVcnLocked( @NonNull ParcelUuid subscriptionGroup, @NonNull VcnConfig config) { - Slog.v(TAG, "Starting or updating VCN config for subGrp: " + subscriptionGroup); + Slog.d(TAG, "Starting or updating VCN config for subGrp: " + subscriptionGroup); if (mVcns.containsKey(subscriptionGroup)) { final Vcn vcn = mVcns.get(subscriptionGroup); @@ -584,7 +585,7 @@ public class VcnManagementService extends IVcnManagementService.Stub { if (!config.getProvisioningPackageName().equals(opPkgName)) { throw new IllegalArgumentException("Mismatched caller and VcnConfig creator"); } - Slog.v(TAG, "VCN config updated for subGrp: " + subscriptionGroup); + Slog.d(TAG, "VCN config updated for subGrp: " + subscriptionGroup); mContext.getSystemService(AppOpsManager.class) .checkPackage(mDeps.getBinderCallingUid(), config.getProvisioningPackageName()); @@ -609,7 +610,7 @@ public class VcnManagementService extends IVcnManagementService.Stub { public void clearVcnConfig(@NonNull ParcelUuid subscriptionGroup, @NonNull String opPkgName) { requireNonNull(subscriptionGroup, "subscriptionGroup was null"); requireNonNull(opPkgName, "opPkgName was null"); - Slog.v(TAG, "VCN config cleared for subGrp: " + subscriptionGroup); + Slog.d(TAG, "VCN config cleared for subGrp: " + subscriptionGroup); mContext.getSystemService(AppOpsManager.class) .checkPackage(mDeps.getBinderCallingUid(), opPkgName); @@ -845,8 +846,14 @@ public class VcnManagementService extends IVcnManagementService.Stub { } final NetworkCapabilities result = ncBuilder.build(); - return new VcnUnderlyingNetworkPolicy( + final VcnUnderlyingNetworkPolicy policy = new VcnUnderlyingNetworkPolicy( mTrackingNetworkCallback.requiresRestartForCarrierWifi(result), result); + + if (VDBG) { + Slog.d(TAG, "getUnderlyingNetworkPolicy() called for caps: " + networkCapabilities + + "; and lp: " + linkProperties + "; result = " + policy); + } + return policy; }); } diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 0d2cd620e73d..0d06426a7ab5 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -9962,6 +9962,8 @@ public class PackageManagerService extends IPackageManager.Stub public boolean performDexOptMode(String packageName, boolean checkProfiles, String targetCompilerFilter, boolean force, boolean bootComplete, String splitName) { + enforceSystemOrRootOrShell("performDexOptMode"); + int flags = (checkProfiles ? DexoptOptions.DEXOPT_CHECK_FOR_PROFILES_UPDATES : 0) | (force ? DexoptOptions.DEXOPT_FORCE : 0) | (bootComplete ? DexoptOptions.DEXOPT_BOOT_COMPLETE : 0); diff --git a/services/core/java/com/android/server/recoverysystem/RecoverySystemService.java b/services/core/java/com/android/server/recoverysystem/RecoverySystemService.java index 12e55e5f1805..ed4a7bf107d1 100644 --- a/services/core/java/com/android/server/recoverysystem/RecoverySystemService.java +++ b/services/core/java/com/android/server/recoverysystem/RecoverySystemService.java @@ -27,6 +27,7 @@ import static android.os.UserHandle.USER_SYSTEM; import static android.ota.nano.OtaPackageMetadata.ApexMetadata; import static com.android.internal.widget.LockSettingsInternal.ARM_REBOOT_ERROR_NONE; +import static com.android.internal.widget.LockSettingsInternal.ARM_REBOOT_ERROR_NO_PROVIDER; import android.annotation.IntDef; import android.apex.CompressedApexInfo; @@ -398,7 +399,13 @@ public class RecoverySystemService extends IRecoverySystem.Stub implements Reboo @VisibleForTesting void onSystemServicesReady() { - mInjector.getLockSettingsService().setRebootEscrowListener(this); + LockSettingsInternal lockSettings = mInjector.getLockSettingsService(); + if (lockSettings == null) { + Slog.e(TAG, "Failed to get lock settings service, skipping set" + + " RebootEscrowListener"); + return; + } + lockSettings.setRebootEscrowListener(this); } @Override // Binder call @@ -564,12 +571,18 @@ public class RecoverySystemService extends IRecoverySystem.Stub implements Reboo case ROR_NEED_PREPARATION: final long origId = Binder.clearCallingIdentity(); try { - boolean result = mInjector.getLockSettingsService().prepareRebootEscrow(); + LockSettingsInternal lockSettings = mInjector.getLockSettingsService(); + if (lockSettings == null) { + Slog.e(TAG, "Failed to get lock settings service, skipping" + + " prepareRebootEscrow"); + return false; + } // Clear the RoR preparation state if lock settings reports an failure. - if (!result) { + if (!lockSettings.prepareRebootEscrow()) { clearRoRPreparationState(); + return false; } - return result; + return true; } finally { Binder.restoreCallingIdentity(origId); } @@ -684,7 +697,14 @@ public class RecoverySystemService extends IRecoverySystem.Stub implements Reboo case ROR_REQUESTED_NEED_CLEAR: final long origId = Binder.clearCallingIdentity(); try { - return mInjector.getLockSettingsService().clearRebootEscrow(); + LockSettingsInternal lockSettings = mInjector.getLockSettingsService(); + if (lockSettings == null) { + Slog.e(TAG, "Failed to get lock settings service, skipping" + + " clearRebootEscrow"); + return false; + } + + return lockSettings.clearRebootEscrow(); } finally { Binder.restoreCallingIdentity(origId); } @@ -778,7 +798,15 @@ public class RecoverySystemService extends IRecoverySystem.Stub implements Reboo final long origId = Binder.clearCallingIdentity(); int providerErrorCode; try { - providerErrorCode = mInjector.getLockSettingsService().armRebootEscrow(); + LockSettingsInternal lockSettings = mInjector.getLockSettingsService(); + if (lockSettings == null) { + Slog.e(TAG, "Failed to get lock settings service, skipping" + + " armRebootEscrow"); + return new RebootPreparationError( + RESUME_ON_REBOOT_REBOOT_ERROR_PROVIDER_PREPARATION_FAILURE, + ARM_REBOOT_ERROR_NO_PROVIDER); + } + providerErrorCode = lockSettings.armRebootEscrow(); } finally { Binder.restoreCallingIdentity(origId); } diff --git a/services/core/java/com/android/server/vcn/Vcn.java b/services/core/java/com/android/server/vcn/Vcn.java index e1747f71d5a9..f918827e9639 100644 --- a/services/core/java/com/android/server/vcn/Vcn.java +++ b/services/core/java/com/android/server/vcn/Vcn.java @@ -50,6 +50,7 @@ import com.android.internal.annotations.VisibleForTesting.Visibility; import com.android.internal.util.IndentingPrintWriter; import com.android.server.VcnManagementService.VcnCallback; import com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot; +import com.android.server.vcn.util.LogUtils; import java.util.Arrays; import java.util.Collections; @@ -305,13 +306,13 @@ public class Vcn extends Handler { handleTeardown(); break; default: - Slog.wtf(getLogTag(), "Unknown msg.what: " + msg.what); + logWtf("Unknown msg.what: " + msg.what); } } private void handleConfigUpdated(@NonNull VcnConfig config) { // TODO: Add a dump function in VcnConfig that omits PII. Until then, use hashCode() - Slog.v(getLogTag(), "Config updated: config = " + config.hashCode()); + logDbg("Config updated: old = " + mConfig.hashCode() + "; new = " + config.hashCode()); mConfig = config; @@ -326,8 +327,7 @@ public class Vcn extends Handler { // connection details may have changed). if (!mConfig.getGatewayConnectionConfigs().contains(gatewayConnectionConfig)) { if (gatewayConnection == null) { - Slog.wtf( - getLogTag(), "Found gatewayConnectionConfig without GatewayConnection"); + logWtf("Found gatewayConnectionConfig without GatewayConnection"); } else { gatewayConnection.teardownAsynchronously(); } @@ -340,6 +340,7 @@ public class Vcn extends Handler { } private void handleTeardown() { + logDbg("Tearing down"); mVcnContext.getVcnNetworkProvider().unregisterListener(mRequestListener); for (VcnGatewayConnection gatewayConnection : mVcnGatewayConnections.values()) { @@ -350,6 +351,7 @@ public class Vcn extends Handler { } private void handleSafeModeStatusChanged() { + logDbg("VcnGatewayConnection safe mode status changed"); boolean hasSafeModeGatewayConnection = false; // If any VcnGatewayConnection is in safe mode, mark the entire VCN as being in safe mode @@ -365,21 +367,19 @@ public class Vcn extends Handler { hasSafeModeGatewayConnection ? VCN_STATUS_CODE_SAFE_MODE : VCN_STATUS_CODE_ACTIVE; if (oldStatus != mCurrentStatus) { mVcnCallback.onSafeModeStatusChanged(hasSafeModeGatewayConnection); + logDbg( + "Safe mode " + + (mCurrentStatus == VCN_STATUS_CODE_SAFE_MODE ? "entered" : "exited")); } } private void handleNetworkRequested(@NonNull NetworkRequest request) { - Slog.v(getLogTag(), "Received request " + request); + logVdbg("Received request " + request); // If preexisting VcnGatewayConnection(s) satisfy request, return for (VcnGatewayConnectionConfig gatewayConnectionConfig : mVcnGatewayConnections.keySet()) { if (isRequestSatisfiedByGatewayConnectionConfig(request, gatewayConnectionConfig)) { - if (VDBG) { - Slog.v( - getLogTag(), - "Request already satisfied by existing VcnGatewayConnection: " - + request); - } + logDbg("Request already satisfied by existing VcnGatewayConnection: " + request); return; } } @@ -389,7 +389,7 @@ public class Vcn extends Handler { for (VcnGatewayConnectionConfig gatewayConnectionConfig : mConfig.getGatewayConnectionConfigs()) { if (isRequestSatisfiedByGatewayConnectionConfig(request, gatewayConnectionConfig)) { - Slog.v(getLogTag(), "Bringing up new VcnGatewayConnection for request " + request); + logDbg("Bringing up new VcnGatewayConnection for request " + request); if (getExposedCapabilitiesForMobileDataState(gatewayConnectionConfig).isEmpty()) { // Skip; this network does not provide any services if mobile data is disabled. @@ -400,8 +400,9 @@ public class Vcn extends Handler { // pre-existing VcnGatewayConnections that satisfy a given request, but if state // that affects the satsifying of requests changes, this is theoretically possible. if (mVcnGatewayConnections.containsKey(gatewayConnectionConfig)) { - Slog.wtf(getLogTag(), "Attempted to bring up VcnGatewayConnection for config " - + "with existing VcnGatewayConnection"); + logWtf( + "Attempted to bring up VcnGatewayConnection for config " + + "with existing VcnGatewayConnection"); return; } @@ -414,8 +415,12 @@ public class Vcn extends Handler { new VcnGatewayStatusCallbackImpl(gatewayConnectionConfig), mIsMobileDataEnabled); mVcnGatewayConnections.put(gatewayConnectionConfig, vcnGatewayConnection); + + return; } } + + logVdbg("Request could not be fulfilled by VCN: " + request); } private Set<Integer> getExposedCapabilitiesForMobileDataState( @@ -432,7 +437,7 @@ public class Vcn extends Handler { } private void handleGatewayConnectionQuit(VcnGatewayConnectionConfig config) { - Slog.v(getLogTag(), "VcnGatewayConnection quit: " + config); + logDbg("VcnGatewayConnection quit: " + config); mVcnGatewayConnections.remove(config); // Trigger a re-evaluation of all NetworkRequests (to make sure any that can be satisfied @@ -467,9 +472,7 @@ public class Vcn extends Handler { if (exposedCaps.contains(NET_CAPABILITY_INTERNET) || exposedCaps.contains(NET_CAPABILITY_DUN)) { if (gatewayConnection == null) { - Slog.wtf( - getLogTag(), - "Found gatewayConnectionConfig without GatewayConnection"); + logWtf("Found gatewayConnectionConfig without" + " GatewayConnection"); } else { // TODO(b/184868850): Optimize by restarting NetworkAgents without teardown. gatewayConnection.teardownAsynchronously(); @@ -479,6 +482,8 @@ public class Vcn extends Handler { // Trigger re-evaluation of all requests; mobile data state impacts supported caps. mVcnContext.getVcnNetworkProvider().resendAllRequests(mRequestListener); + + logDbg("Mobile data " + (mIsMobileDataEnabled ? "enabled" : "disabled")); } } @@ -507,8 +512,38 @@ public class Vcn extends Handler { return request.canBeSatisfiedBy(builder.build()); } - private String getLogTag() { - return TAG + " [" + mSubscriptionGroup.hashCode() + "]"; + private String getLogPrefix() { + return "[" + LogUtils.getHashedSubscriptionGroup(mSubscriptionGroup) + "]: "; + } + + private void logVdbg(String msg) { + if (VDBG) { + Slog.v(TAG, getLogPrefix() + msg); + } + } + + private void logDbg(String msg) { + Slog.d(TAG, getLogPrefix() + msg); + } + + private void logDbg(String msg, Throwable tr) { + Slog.d(TAG, getLogPrefix() + msg, tr); + } + + private void logErr(String msg) { + Slog.e(TAG, getLogPrefix() + msg); + } + + private void logErr(String msg, Throwable tr) { + Slog.e(TAG, getLogPrefix() + msg, tr); + } + + private void logWtf(String msg) { + Slog.wtf(TAG, getLogPrefix() + msg); + } + + private void logWtf(String msg, Throwable tr) { + Slog.wtf(TAG, getLogPrefix() + msg, tr); } /** @@ -521,6 +556,7 @@ public class Vcn extends Handler { pw.increaseIndent(); pw.println("mCurrentStatus: " + mCurrentStatus); + pw.println("mIsMobileDataEnabled: " + mIsMobileDataEnabled); pw.println("mVcnGatewayConnections:"); for (VcnGatewayConnection gw : mVcnGatewayConnections.values()) { diff --git a/services/core/java/com/android/server/vcn/VcnGatewayConnection.java b/services/core/java/com/android/server/vcn/VcnGatewayConnection.java index 65b947c1fcdd..5cecff6f93c1 100644 --- a/services/core/java/com/android/server/vcn/VcnGatewayConnection.java +++ b/services/core/java/com/android/server/vcn/VcnGatewayConnection.java @@ -88,6 +88,7 @@ import com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscription import com.android.server.vcn.UnderlyingNetworkTracker.UnderlyingNetworkRecord; import com.android.server.vcn.UnderlyingNetworkTracker.UnderlyingNetworkTrackerCallback; import com.android.server.vcn.Vcn.VcnGatewayStatusCallback; +import com.android.server.vcn.util.LogUtils; import com.android.server.vcn.util.MtuUtils; import java.io.IOException; @@ -701,6 +702,7 @@ public class VcnGatewayConnection extends StateMachine { * <p>Once torn down, this VcnTunnel CANNOT be started again. */ public void teardownAsynchronously() { + logDbg("Triggering async teardown"); sendDisconnectRequestedAndAcquireWakelock( DISCONNECT_REASON_TEARDOWN, true /* shouldQuit */); @@ -710,6 +712,8 @@ public class VcnGatewayConnection extends StateMachine { @Override protected void onQuitting() { + logDbg("Quitting VcnGatewayConnection"); + // No need to call setInterfaceDown(); the IpSecInterface is being fully torn down. if (mTunnelIface != null) { mTunnelIface.close(); @@ -750,6 +754,10 @@ public class VcnGatewayConnection extends StateMachine { // TODO(b/180132994): explore safely removing this Thread check mVcnContext.ensureRunningOnLooperThread(); + logDbg( + "Selected underlying network changed: " + + (underlying == null ? null : underlying.network)); + // TODO(b/179091925): Move the delayed-message handling to BaseState // If underlying is null, all underlying networks have been lost. Disconnect VCN after a @@ -774,6 +782,8 @@ public class VcnGatewayConnection extends StateMachine { if (!mIsQuitting) { mWakeLock.acquire(); + + logVdbg("Wakelock acquired: " + mWakeLock); } } @@ -781,6 +791,8 @@ public class VcnGatewayConnection extends StateMachine { mVcnContext.ensureRunningOnLooperThread(); mWakeLock.release(); + + logVdbg("Wakelock released: " + mWakeLock); } /** @@ -798,8 +810,7 @@ public class VcnGatewayConnection extends StateMachine { @Override public void sendMessage(int what) { - Slog.wtf( - TAG, + logWtf( "sendMessage should not be used in VcnGatewayConnection. See" + " sendMessageAndAcquireWakeLock()"); super.sendMessage(what); @@ -807,8 +818,7 @@ public class VcnGatewayConnection extends StateMachine { @Override public void sendMessage(int what, Object obj) { - Slog.wtf( - TAG, + logWtf( "sendMessage should not be used in VcnGatewayConnection. See" + " sendMessageAndAcquireWakeLock()"); super.sendMessage(what, obj); @@ -816,8 +826,7 @@ public class VcnGatewayConnection extends StateMachine { @Override public void sendMessage(int what, int arg1) { - Slog.wtf( - TAG, + logWtf( "sendMessage should not be used in VcnGatewayConnection. See" + " sendMessageAndAcquireWakeLock()"); super.sendMessage(what, arg1); @@ -825,8 +834,7 @@ public class VcnGatewayConnection extends StateMachine { @Override public void sendMessage(int what, int arg1, int arg2) { - Slog.wtf( - TAG, + logWtf( "sendMessage should not be used in VcnGatewayConnection. See" + " sendMessageAndAcquireWakeLock()"); super.sendMessage(what, arg1, arg2); @@ -834,8 +842,7 @@ public class VcnGatewayConnection extends StateMachine { @Override public void sendMessage(int what, int arg1, int arg2, Object obj) { - Slog.wtf( - TAG, + logWtf( "sendMessage should not be used in VcnGatewayConnection. See" + " sendMessageAndAcquireWakeLock()"); super.sendMessage(what, arg1, arg2, obj); @@ -843,8 +850,7 @@ public class VcnGatewayConnection extends StateMachine { @Override public void sendMessage(Message msg) { - Slog.wtf( - TAG, + logWtf( "sendMessage should not be used in VcnGatewayConnection. See" + " sendMessageAndAcquireWakeLock()"); super.sendMessage(msg); @@ -935,10 +941,14 @@ public class VcnGatewayConnection extends StateMachine { } private void setTeardownTimeoutAlarm() { + logVdbg("Setting teardown timeout alarm; mCurrentToken: " + mCurrentToken); + // Safe to assign this alarm because it is either 1) already null, or 2) already fired. In // either case, there is nothing to cancel. if (mTeardownTimeoutAlarm != null) { - Slog.wtf(TAG, "mTeardownTimeoutAlarm should be null before being set"); + logWtf( + "mTeardownTimeoutAlarm should be null before being set; mCurrentToken: " + + mCurrentToken); } final Message delayedMessage = obtainMessage(EVENT_TEARDOWN_TIMEOUT_EXPIRED, mCurrentToken); @@ -950,6 +960,8 @@ public class VcnGatewayConnection extends StateMachine { } private void cancelTeardownTimeoutAlarm() { + logVdbg("Cancelling teardown timeout alarm; mCurrentToken: " + mCurrentToken); + if (mTeardownTimeoutAlarm != null) { mTeardownTimeoutAlarm.cancel(); mTeardownTimeoutAlarm = null; @@ -960,6 +972,11 @@ public class VcnGatewayConnection extends StateMachine { } private void setDisconnectRequestAlarm() { + logVdbg( + "Setting alarm to disconnect due to underlying network loss;" + + " mCurrentToken: " + + mCurrentToken); + // Only schedule a NEW alarm if none is already set. if (mDisconnectRequestAlarm != null) { return; @@ -980,6 +997,11 @@ public class VcnGatewayConnection extends StateMachine { } private void cancelDisconnectRequestAlarm() { + logVdbg( + "Cancelling alarm to disconnect due to underlying network loss;" + + " mCurrentToken: " + + mCurrentToken); + if (mDisconnectRequestAlarm != null) { mDisconnectRequestAlarm.cancel(); mDisconnectRequestAlarm = null; @@ -993,10 +1015,14 @@ public class VcnGatewayConnection extends StateMachine { } private void setRetryTimeoutAlarm(long delay) { + logVdbg("Setting retry alarm; mCurrentToken: " + mCurrentToken); + // Safe to assign this alarm because it is either 1) already null, or 2) already fired. In // either case, there is nothing to cancel. if (mRetryTimeoutAlarm != null) { - Slog.wtf(TAG, "mRetryTimeoutAlarm should be null before being set"); + logWtf( + "mRetryTimeoutAlarm should be null before being set; mCurrentToken: " + + mCurrentToken); } final Message delayedMessage = obtainMessage(EVENT_RETRY_TIMEOUT_EXPIRED, mCurrentToken); @@ -1004,6 +1030,8 @@ public class VcnGatewayConnection extends StateMachine { } private void cancelRetryTimeoutAlarm() { + logVdbg("Cancel retry alarm; mCurrentToken: " + mCurrentToken); + if (mRetryTimeoutAlarm != null) { mRetryTimeoutAlarm.cancel(); mRetryTimeoutAlarm = null; @@ -1014,6 +1042,8 @@ public class VcnGatewayConnection extends StateMachine { @VisibleForTesting(visibility = Visibility.PRIVATE) void setSafeModeAlarm() { + logVdbg("Setting safe mode alarm; mCurrentToken: " + mCurrentToken); + // Only schedule a NEW alarm if none is already set. if (mSafeModeTimeoutAlarm != null) { return; @@ -1028,6 +1058,8 @@ public class VcnGatewayConnection extends StateMachine { } private void cancelSafeModeAlarm() { + logVdbg("Cancel safe mode alarm; mCurrentToken: " + mCurrentToken); + if (mSafeModeTimeoutAlarm != null) { mSafeModeTimeoutAlarm.cancel(); mSafeModeTimeoutAlarm = null; @@ -1092,6 +1124,14 @@ public class VcnGatewayConnection extends StateMachine { + exception.getMessage(); } + logDbg( + "Encountered error; code=" + + errorCode + + ", exceptionClass=" + + exceptionClass + + ", exceptionMessage=" + + exceptionMessage); + mGatewayStatusCallback.onGatewayConnectionError( mConnectionConfig.getGatewayConnectionName(), errorCode, @@ -1137,7 +1177,7 @@ public class VcnGatewayConnection extends StateMachine { try { enterState(); } catch (Exception e) { - Slog.wtf(TAG, "Uncaught exception", e); + logWtf("Uncaught exception", e); sendDisconnectRequestedAndAcquireWakelock( DISCONNECT_REASON_INTERNAL_ERROR + e.toString(), true /* shouldQuit */); } @@ -1169,14 +1209,14 @@ public class VcnGatewayConnection extends StateMachine { public final boolean processMessage(Message msg) { final int token = msg.arg1; if (!isValidToken(token)) { - Slog.v(TAG, "Message called with obsolete token: " + token + "; what: " + msg.what); + logDbg("Message called with obsolete token: " + token + "; what: " + msg.what); return HANDLED; } try { processStateMsg(msg); } catch (Exception e) { - Slog.wtf(TAG, "Uncaught exception", e); + logWtf("Uncaught exception", e); sendDisconnectRequestedAndAcquireWakelock( DISCONNECT_REASON_INTERNAL_ERROR + e.toString(), true /* shouldQuit */); } @@ -1194,7 +1234,7 @@ public class VcnGatewayConnection extends StateMachine { try { exitState(); } catch (Exception e) { - Slog.wtf(TAG, "Uncaught exception", e); + logWtf("Uncaught exception", e); sendDisconnectRequestedAndAcquireWakelock( DISCONNECT_REASON_INTERNAL_ERROR + e.toString(), true /* shouldQuit */); } @@ -1234,7 +1274,7 @@ public class VcnGatewayConnection extends StateMachine { protected void handleDisconnectRequested(EventDisconnectRequestedInfo info) { // TODO(b/180526152): notify VcnStatusCallback for Network loss - Slog.v(TAG, "Tearing down. Cause: " + info.reason); + logDbg("Tearing down. Cause: " + info.reason); mIsQuitting = info.shouldQuit; teardownNetwork(); @@ -1250,6 +1290,7 @@ public class VcnGatewayConnection extends StateMachine { protected void handleSafeModeTimeoutExceeded() { mSafeModeTimeoutAlarm = null; + logDbg("Entering safe mode after timeout exceeded"); // Connectivity for this GatewayConnection is broken; tear down the Network. teardownNetwork(); @@ -1258,13 +1299,15 @@ public class VcnGatewayConnection extends StateMachine { } protected void logUnexpectedEvent(int what) { - Slog.d(TAG, String.format( - "Unexpected event code %d in state %s", what, this.getClass().getSimpleName())); + logDbg( + "Unexpected event code " + + what + + " in state " + + this.getClass().getSimpleName()); } protected void logWtfUnknownEvent(int what) { - Slog.wtf(TAG, String.format( - "Unknown event code %d in state %s", what, this.getClass().getSimpleName())); + logWtf("Unknown event code " + what + " in state " + this.getClass().getSimpleName()); } } @@ -1281,7 +1324,7 @@ public class VcnGatewayConnection extends StateMachine { } if (mIkeSession != null || mNetworkAgent != null) { - Slog.wtf(TAG, "Active IKE Session or NetworkAgent in DisconnectedState"); + logWtf("Active IKE Session or NetworkAgent in DisconnectedState"); } cancelSafeModeAlarm(); @@ -1349,7 +1392,7 @@ public class VcnGatewayConnection extends StateMachine { @Override protected void enterState() throws Exception { if (mIkeSession == null) { - Slog.wtf(TAG, "IKE session was already closed when entering Disconnecting state."); + logWtf("IKE session was already closed when entering Disconnecting state."); sendMessageAndAcquireWakeLock(EVENT_SESSION_CLOSED, mCurrentToken); return; } @@ -1436,7 +1479,7 @@ public class VcnGatewayConnection extends StateMachine { @Override protected void enterState() { if (mIkeSession != null) { - Slog.wtf(TAG, "ConnectingState entered with active session"); + logWtf("ConnectingState entered with active session"); // Attempt to recover. mIkeSession.kill(); @@ -1455,7 +1498,7 @@ public class VcnGatewayConnection extends StateMachine { if (oldUnderlying == null) { // This should never happen, but if it does, there's likely a nasty bug. - Slog.wtf(TAG, "Old underlying network was null in connected state. Bug?"); + logWtf("Old underlying network was null in connected state. Bug?"); } // If new underlying is null, all underlying networks have been lost; disconnect @@ -1550,11 +1593,11 @@ public class VcnGatewayConnection extends StateMachine { // new NetworkAgent replaces an old one before the unwanted() call // is processed. if (mNetworkAgent != agentRef) { - Slog.d(TAG, "unwanted() called on stale NetworkAgent"); + logDbg("unwanted() called on stale NetworkAgent"); return; } - Slog.d(TAG, "NetworkAgent was unwanted"); + logDbg("NetworkAgent was unwanted"); teardownAsynchronously(); } /* networkUnwantedCallback */, (status) -> { @@ -1568,8 +1611,7 @@ public class VcnGatewayConnection extends StateMachine { setSafeModeAlarm(); break; default: - Slog.wtf( - TAG, + logWtf( "Unknown validation status " + status + "; ignoring"); @@ -1602,13 +1644,26 @@ public class VcnGatewayConnection extends StateMachine { @NonNull Network underlyingNetwork, @NonNull IpSecTransform transform, int direction) { + if (direction != IpSecManager.DIRECTION_IN && direction != IpSecManager.DIRECTION_OUT) { + Slog.wtf(TAG, "Applying transform for unexpected direction: " + direction); + } + try { tunnelIface.setUnderlyingNetwork(underlyingNetwork); // Transforms do not need to be persisted; the IkeSession will keep them alive mIpSecManager.applyTunnelModeTransform(tunnelIface, direction, transform); + + // For inbound transforms, additionally allow forwarded traffic to bridge to DUN (as + // needed) + final Set<Integer> exposedCaps = mConnectionConfig.getAllExposedCapabilities(); + if (direction == IpSecManager.DIRECTION_IN + && exposedCaps.contains(NET_CAPABILITY_DUN)) { + mIpSecManager.applyTunnelModeTransform( + tunnelIface, IpSecManager.DIRECTION_FWD, transform); + } } catch (IOException e) { - Slog.d(TAG, "Transform application failed for network " + token, e); + logDbg("Transform application failed for network " + token, e); sessionLost(token, e); } } @@ -1642,7 +1697,7 @@ public class VcnGatewayConnection extends StateMachine { tunnelIface.removeAddress(address.getAddress(), address.getPrefixLength()); } } catch (IOException e) { - Slog.d(TAG, "Adding address to tunnel failed for token " + token, e); + logDbg("Adding address to tunnel failed for token " + token, e); sessionLost(token, e); } } @@ -1722,6 +1777,8 @@ public class VcnGatewayConnection extends StateMachine { } private void handleMigrationCompleted(EventMigrationCompletedInfo migrationCompletedInfo) { + logDbg("Migration completed: " + mUnderlying.network); + applyTransform( mCurrentToken, mTunnelIface, @@ -1744,6 +1801,8 @@ public class VcnGatewayConnection extends StateMachine { mUnderlying = ((EventUnderlyingNetworkChangedInfo) msg.obj).newUnderlying; if (mUnderlying == null) { + logDbg("Underlying network lost"); + // Ignored for now; a new network may be coming up. If none does, the delayed // NETWORK_LOST disconnect will be fired, and tear down the session + network. return; @@ -1752,7 +1811,7 @@ public class VcnGatewayConnection extends StateMachine { // mUnderlying assumed non-null, given check above. // If network changed, migrate. Otherwise, update any existing networkAgent. if (oldUnderlying == null || !oldUnderlying.network.equals(mUnderlying.network)) { - Slog.v(TAG, "Migrating to new network: " + mUnderlying.network); + logDbg("Migrating to new network: " + mUnderlying.network); mIkeSession.setNetwork(mUnderlying.network); } else { // oldUnderlying is non-null & underlying network itself has not changed @@ -1803,7 +1862,7 @@ public class VcnGatewayConnection extends StateMachine { mFailedAttempts++; if (mUnderlying == null) { - Slog.wtf(TAG, "Underlying network was null in retry state"); + logWtf("Underlying network was null in retry state"); transitionTo(mDisconnectedState); } else { // Safe to blindly set up, as it is cancelled and cleared on exiting this state @@ -1973,25 +2032,25 @@ public class VcnGatewayConnection extends StateMachine { @Override public void onOpened(@NonNull IkeSessionConfiguration ikeSessionConfig) { - Slog.v(TAG, "IkeOpened for token " + mToken); + logDbg("IkeOpened for token " + mToken); // Nothing to do here. } @Override public void onClosed() { - Slog.v(TAG, "IkeClosed for token " + mToken); + logDbg("IkeClosed for token " + mToken); sessionClosed(mToken, null); } @Override public void onClosedExceptionally(@NonNull IkeException exception) { - Slog.v(TAG, "IkeClosedExceptionally for token " + mToken, exception); + logDbg("IkeClosedExceptionally for token " + mToken, exception); sessionClosed(mToken, exception); } @Override public void onError(@NonNull IkeProtocolException exception) { - Slog.v(TAG, "IkeError for token " + mToken, exception); + logDbg("IkeError for token " + mToken, exception); // Non-fatal, log and continue. } } @@ -2008,7 +2067,7 @@ public class VcnGatewayConnection extends StateMachine { /** Internal proxy method for injecting of mocked ChildSessionConfiguration */ @VisibleForTesting(visibility = Visibility.PRIVATE) void onOpened(@NonNull VcnChildSessionConfiguration childConfig) { - Slog.v(TAG, "ChildOpened for token " + mToken); + logDbg("ChildOpened for token " + mToken); childOpened(mToken, childConfig); } @@ -2019,19 +2078,19 @@ public class VcnGatewayConnection extends StateMachine { @Override public void onClosed() { - Slog.v(TAG, "ChildClosed for token " + mToken); + logDbg("ChildClosed for token " + mToken); sessionLost(mToken, null); } @Override public void onClosedExceptionally(@NonNull IkeException exception) { - Slog.v(TAG, "ChildClosedExceptionally for token " + mToken, exception); + logDbg("ChildClosedExceptionally for token " + mToken, exception); sessionLost(mToken, exception); } @Override public void onIpSecTransformCreated(@NonNull IpSecTransform transform, int direction) { - Slog.v(TAG, "ChildTransformCreated; Direction: " + direction + "; token " + mToken); + logDbg("ChildTransformCreated; Direction: " + direction + "; token " + mToken); childTransformCreated(mToken, transform, direction); } @@ -2039,7 +2098,7 @@ public class VcnGatewayConnection extends StateMachine { public void onIpSecTransformsMigrated( @NonNull IpSecTransform inIpSecTransform, @NonNull IpSecTransform outIpSecTransform) { - Slog.v(TAG, "ChildTransformsMigrated; token " + mToken); + logDbg("ChildTransformsMigrated; token " + mToken); migrationCompleted(mToken, inIpSecTransform, outIpSecTransform); } @@ -2047,10 +2106,48 @@ public class VcnGatewayConnection extends StateMachine { public void onIpSecTransformDeleted(@NonNull IpSecTransform transform, int direction) { // Nothing to be done; no references to the IpSecTransform are held, and this transform // will be closed by the IKE library. - Slog.v(TAG, "ChildTransformDeleted; Direction: " + direction + "; for token " + mToken); + logDbg("ChildTransformDeleted; Direction: " + direction + "; for token " + mToken); } } + private String getLogPrefix() { + return "[" + + LogUtils.getHashedSubscriptionGroup(mSubscriptionGroup) + + "-" + + mConnectionConfig.getGatewayConnectionName() + + "]: "; + } + + private void logVdbg(String msg) { + if (VDBG) { + Slog.v(TAG, getLogPrefix() + msg); + } + } + + private void logDbg(String msg) { + Slog.d(TAG, getLogPrefix() + msg); + } + + private void logDbg(String msg, Throwable tr) { + Slog.d(TAG, getLogPrefix() + msg, tr); + } + + private void logErr(String msg) { + Slog.e(TAG, getLogPrefix() + msg); + } + + private void logErr(String msg, Throwable tr) { + Slog.e(TAG, getLogPrefix() + msg, tr); + } + + private void logWtf(String msg) { + Slog.wtf(TAG, getLogPrefix() + msg); + } + + private void logWtf(String msg, Throwable tr) { + Slog.wtf(TAG, getLogPrefix() + msg, tr); + } + /** * Dumps the state of this VcnGatewayConnection for logging and debugging purposes. * diff --git a/services/core/java/com/android/server/vcn/util/LogUtils.java b/services/core/java/com/android/server/vcn/util/LogUtils.java new file mode 100644 index 000000000000..93728ceb27c5 --- /dev/null +++ b/services/core/java/com/android/server/vcn/util/LogUtils.java @@ -0,0 +1,39 @@ +/* + * 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.vcn.util; + +import android.annotation.Nullable; +import android.os.ParcelUuid; + +import com.android.internal.util.HexDump; + +/** @hide */ +public class LogUtils { + /** + * Returns the hash of the subscription group in hexadecimal format. + * + * @return the hexadecimal encoded string if uuid was non-null, else {@code null} + */ + @Nullable + public static String getHashedSubscriptionGroup(@Nullable ParcelUuid uuid) { + if (uuid == null) { + return null; + } + + return HexDump.toHexString(uuid.hashCode()); + } +} diff --git a/telephony/java/android/telephony/ims/ImsMmTelManager.java b/telephony/java/android/telephony/ims/ImsMmTelManager.java index d1a893f61e00..740332708dbc 100644 --- a/telephony/java/android/telephony/ims/ImsMmTelManager.java +++ b/telephony/java/android/telephony/ims/ImsMmTelManager.java @@ -713,15 +713,8 @@ public class ImsMmTelManager implements RegistrationManager { * @see android.telephony.CarrierConfigManager#KEY_CARRIER_VT_AVAILABLE_BOOL * @see android.telephony.CarrierConfigManager#KEY_CARRIER_IMS_GBA_REQUIRED_BOOL * @see #isAvailable(int, int) - * - * @param imsRegTech The IMS registration technology, can be one of the following: - * {@link ImsRegistrationImplBase#REGISTRATION_TECH_LTE}, - * {@link ImsRegistrationImplBase#REGISTRATION_TECH_IWLAN} - * @param capability The IMS MmTel capability to query, can be one of the following: - * {@link MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_VOICE}, - * {@link MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_VIDEO}, - * {@link MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_UT}, - * {@link MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_SMS} + * @param imsRegTech The IMS registration technology. + * @param capability The IMS MmTel capability to query. * @return {@code true} if the MmTel IMS capability is capable for this subscription, false * otherwise. * @hide @@ -748,14 +741,8 @@ public class ImsMmTelManager implements RegistrationManager { * * @see #isCapable(int, int) * - * @param imsRegTech The IMS registration technology, can be one of the following: - * {@link ImsRegistrationImplBase#REGISTRATION_TECH_LTE}, - * {@link ImsRegistrationImplBase#REGISTRATION_TECH_IWLAN} - * @param capability The IMS MmTel capability to query, can be one of the following: - * {@link MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_VOICE}, - * {@link MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_VIDEO}, - * {@link MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_UT}, - * {@link MmTelFeature.MmTelCapabilities#CAPABILITY_TYPE_SMS} + * @param imsRegTech The IMS registration technology. + * @param capability The IMS MmTel capability to query. * @return {@code true} if the MmTel IMS capability is available for this subscription, false * otherwise. * @hide diff --git a/telephony/java/android/telephony/ims/RegistrationManager.java b/telephony/java/android/telephony/ims/RegistrationManager.java index c49121f4dc5d..4713b7660e3a 100644 --- a/telephony/java/android/telephony/ims/RegistrationManager.java +++ b/telephony/java/android/telephony/ims/RegistrationManager.java @@ -82,6 +82,8 @@ public interface RegistrationManager { AccessNetworkConstants.TRANSPORT_TYPE_INVALID); put(ImsRegistrationImplBase.REGISTRATION_TECH_LTE, AccessNetworkConstants.TRANSPORT_TYPE_WWAN); + put(ImsRegistrationImplBase.REGISTRATION_TECH_NR, + AccessNetworkConstants.TRANSPORT_TYPE_WWAN); put(ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN, AccessNetworkConstants.TRANSPORT_TYPE_WLAN); }}; diff --git a/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java b/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java index 23032f0c4d38..35324a3c7975 100644 --- a/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java +++ b/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java @@ -56,7 +56,8 @@ public class ImsRegistrationImplBase { @IntDef(value = { REGISTRATION_TECH_NONE, REGISTRATION_TECH_LTE, - REGISTRATION_TECH_IWLAN + REGISTRATION_TECH_IWLAN, + REGISTRATION_TECH_NR }) @Retention(RetentionPolicy.SOURCE) public @interface ImsRegistrationTech {} @@ -65,13 +66,18 @@ public class ImsRegistrationImplBase { */ public static final int REGISTRATION_TECH_NONE = -1; /** - * IMS is registered to IMS via LTE. + * This ImsService is registered to IMS via LTE. */ public static final int REGISTRATION_TECH_LTE = 0; /** - * IMS is registered to IMS via IWLAN. + * This ImsService is registered to IMS via IWLAN. */ public static final int REGISTRATION_TECH_IWLAN = 1; + // REGISTRATION_TECH = 2 omitted purposefully. + /** + * This ImsService is registered to IMS via NR. + */ + public static final int REGISTRATION_TECH_NR = 3; // Registration states, used to notify new ImsRegistrationImplBase#Callbacks of the current // state. diff --git a/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java b/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java index 32c95f149979..cf2c9c783ac7 100644 --- a/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java +++ b/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java @@ -16,9 +16,14 @@ package com.android.server; +import static android.content.pm.PackageManager.PERMISSION_DENIED; import static android.content.pm.PackageManager.PERMISSION_GRANTED; import static android.net.INetd.IF_STATE_DOWN; import static android.net.INetd.IF_STATE_UP; +import static android.net.IpSecManager.DIRECTION_FWD; +import static android.net.IpSecManager.DIRECTION_IN; +import static android.net.IpSecManager.DIRECTION_OUT; +import static android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK; import static android.system.OsConstants.AF_INET; import static android.system.OsConstants.AF_INET6; @@ -56,6 +61,7 @@ import android.os.Binder; import android.os.ParcelFileDescriptor; import android.system.Os; import android.test.mock.MockContext; +import android.util.ArraySet; import androidx.test.filters.SmallTest; @@ -71,6 +77,7 @@ import java.net.Inet4Address; import java.net.Socket; import java.util.Arrays; import java.util.Collection; +import java.util.Set; /** Unit tests for {@link IpSecService}. */ @SmallTest @@ -119,7 +126,18 @@ public class IpSecServiceParameterizedTest { AppOpsManager mMockAppOps = mock(AppOpsManager.class); ConnectivityManager mMockConnectivityMgr = mock(ConnectivityManager.class); - MockContext mMockContext = new MockContext() { + TestContext mTestContext = new TestContext(); + + private class TestContext extends MockContext { + private Set<String> mAllowedPermissions = new ArraySet<>(Arrays.asList( + android.Manifest.permission.MANAGE_IPSEC_TUNNELS, + android.Manifest.permission.NETWORK_STACK, + PERMISSION_MAINLINE_NETWORK_STACK)); + + private void setAllowedPermissions(String... permissions) { + mAllowedPermissions = new ArraySet<>(permissions); + } + @Override public Object getSystemService(String name) { switch(name) { @@ -147,20 +165,22 @@ public class IpSecServiceParameterizedTest { @Override public void enforceCallingOrSelfPermission(String permission, String message) { - if (permission == android.Manifest.permission.MANAGE_IPSEC_TUNNELS) { + if (mAllowedPermissions.contains(permission)) { return; + } else { + throw new SecurityException("Unavailable permission requested"); } - throw new SecurityException("Unavailable permission requested"); } @Override public int checkCallingOrSelfPermission(String permission) { - if (android.Manifest.permission.NETWORK_STACK.equals(permission)) { + if (mAllowedPermissions.contains(permission)) { return PERMISSION_GRANTED; + } else { + return PERMISSION_DENIED; } - throw new UnsupportedOperationException(); } - }; + } INetd mMockNetd; PackageManager mMockPkgMgr; @@ -194,7 +214,7 @@ public class IpSecServiceParameterizedTest { mMockNetd = mock(INetd.class); mMockPkgMgr = mock(PackageManager.class); mMockIpSecSrvConfig = mock(IpSecService.IpSecServiceConfiguration.class); - mIpSecService = new IpSecService(mMockContext, mMockIpSecSrvConfig); + mIpSecService = new IpSecService(mTestContext, mMockIpSecSrvConfig); // Injecting mock netd when(mMockIpSecSrvConfig.getNetdInstance()).thenReturn(mMockNetd); @@ -664,6 +684,21 @@ public class IpSecServiceParameterizedTest { assertNotNull(createTunnelResp); assertEquals(IpSecManager.Status.OK, createTunnelResp.status); + for (int direction : new int[] {DIRECTION_IN, DIRECTION_OUT, DIRECTION_FWD}) { + for (int selAddrFamily : ADDRESS_FAMILIES) { + verify(mMockNetd).ipSecAddSecurityPolicy( + eq(mUid), + eq(selAddrFamily), + eq(direction), + anyString(), + anyString(), + eq(0), + anyInt(), // iKey/oKey + anyInt(), // mask + eq(createTunnelResp.resourceId)); + } + } + return createTunnelResp; } @@ -798,16 +833,51 @@ public class IpSecServiceParameterizedTest { } @Test - public void testApplyTunnelModeTransform() throws Exception { - verifyApplyTunnelModeTransformCommon(false); + public void testApplyTunnelModeTransformOutbound() throws Exception { + verifyApplyTunnelModeTransformCommon(false /* closeSpiBeforeApply */, DIRECTION_OUT); } @Test - public void testApplyTunnelModeTransformReleasedSpi() throws Exception { - verifyApplyTunnelModeTransformCommon(true); + public void testApplyTunnelModeTransformOutboundNonNetworkStack() throws Exception { + mTestContext.setAllowedPermissions(android.Manifest.permission.MANAGE_IPSEC_TUNNELS); + verifyApplyTunnelModeTransformCommon(false /* closeSpiBeforeApply */, DIRECTION_OUT); } - public void verifyApplyTunnelModeTransformCommon(boolean closeSpiBeforeApply) throws Exception { + @Test + public void testApplyTunnelModeTransformOutboundReleasedSpi() throws Exception { + verifyApplyTunnelModeTransformCommon(true /* closeSpiBeforeApply */, DIRECTION_OUT); + } + + @Test + public void testApplyTunnelModeTransformInbound() throws Exception { + verifyApplyTunnelModeTransformCommon(true /* closeSpiBeforeApply */, DIRECTION_IN); + } + + @Test + public void testApplyTunnelModeTransformInboundNonNetworkStack() throws Exception { + mTestContext.setAllowedPermissions(android.Manifest.permission.MANAGE_IPSEC_TUNNELS); + verifyApplyTunnelModeTransformCommon(true /* closeSpiBeforeApply */, DIRECTION_IN); + } + + @Test + public void testApplyTunnelModeTransformForward() throws Exception { + verifyApplyTunnelModeTransformCommon(true /* closeSpiBeforeApply */, DIRECTION_FWD); + } + + @Test + public void testApplyTunnelModeTransformForwardNonNetworkStack() throws Exception { + mTestContext.setAllowedPermissions(android.Manifest.permission.MANAGE_IPSEC_TUNNELS); + + try { + verifyApplyTunnelModeTransformCommon(true /* closeSpiBeforeApply */, DIRECTION_FWD); + fail("Expected security exception due to use of forward policies without NETWORK_STACK" + + " or MAINLINE_NETWORK_STACK permission"); + } catch (SecurityException expected) { + } + } + + public void verifyApplyTunnelModeTransformCommon(boolean closeSpiBeforeApply, int direction) + throws Exception { IpSecConfig ipSecConfig = new IpSecConfig(); ipSecConfig.setMode(IpSecTransform.MODE_TUNNEL); addDefaultSpisAndRemoteAddrToIpSecConfig(ipSecConfig); @@ -825,17 +895,17 @@ public class IpSecServiceParameterizedTest { int transformResourceId = createTransformResp.resourceId; int tunnelResourceId = createTunnelResp.resourceId; mIpSecService.applyTunnelModeTransform( - tunnelResourceId, IpSecManager.DIRECTION_OUT, transformResourceId, BLESSED_PACKAGE); + tunnelResourceId, direction, transformResourceId, BLESSED_PACKAGE); for (int selAddrFamily : ADDRESS_FAMILIES) { verify(mMockNetd) .ipSecUpdateSecurityPolicy( eq(mUid), eq(selAddrFamily), - eq(IpSecManager.DIRECTION_OUT), + eq(direction), anyString(), anyString(), - eq(TEST_SPI), + eq(direction == DIRECTION_OUT ? TEST_SPI : 0), anyInt(), // iKey/oKey anyInt(), // mask eq(tunnelResourceId)); diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java index eedaac48293c..39f7386cbb76 100644 --- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java +++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java @@ -16,8 +16,11 @@ package com.android.server.vcn; +import static android.net.IpSecManager.DIRECTION_FWD; import static android.net.IpSecManager.DIRECTION_IN; import static android.net.IpSecManager.DIRECTION_OUT; +import static android.net.NetworkCapabilities.NET_CAPABILITY_DUN; +import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET; import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR; import static android.net.NetworkCapabilities.TRANSPORT_WIFI; import static android.net.ipsec.ike.exceptions.IkeProtocolException.ERROR_TYPE_AUTHENTICATION_FAILED; @@ -54,6 +57,8 @@ import android.net.ipsec.ike.ChildSaProposal; import android.net.ipsec.ike.exceptions.IkeException; import android.net.ipsec.ike.exceptions.IkeInternalException; import android.net.ipsec.ike.exceptions.IkeProtocolException; +import android.net.vcn.VcnGatewayConnectionConfig; +import android.net.vcn.VcnGatewayConnectionConfigTest; import android.net.vcn.VcnManager.VcnErrorCode; import androidx.test.filters.SmallTest; @@ -143,8 +148,9 @@ public class VcnGatewayConnectionConnectedStateTest extends VcnGatewayConnection assertEquals(mGatewayConnection.mConnectedState, mGatewayConnection.getCurrentState()); } - @Test - public void testCreatedTransformsAreApplied() throws Exception { + private void verifyVcnTransformsApplied( + VcnGatewayConnection vcnGatewayConnection, boolean expectForwardTransform) + throws Exception { for (int direction : new int[] {DIRECTION_IN, DIRECTION_OUT}) { getChildSessionCallback().onIpSecTransformCreated(makeDummyIpSecTransform(), direction); mTestLooper.dispatchAll(); @@ -154,7 +160,40 @@ public class VcnGatewayConnectionConnectedStateTest extends VcnGatewayConnection eq(TEST_IPSEC_TUNNEL_RESOURCE_ID), eq(direction), anyInt(), any()); } - assertEquals(mGatewayConnection.mConnectedState, mGatewayConnection.getCurrentState()); + verify(mIpSecSvc, expectForwardTransform ? times(1) : never()) + .applyTunnelModeTransform( + eq(TEST_IPSEC_TUNNEL_RESOURCE_ID), eq(DIRECTION_FWD), anyInt(), any()); + + assertEquals(vcnGatewayConnection.mConnectedState, vcnGatewayConnection.getCurrentState()); + } + + @Test + public void testCreatedTransformsAreApplied() throws Exception { + verifyVcnTransformsApplied(mGatewayConnection, false /* expectForwardTransform */); + } + + @Test + public void testCreatedTransformsAreAppliedWithDun() throws Exception { + VcnGatewayConnectionConfig gatewayConfig = + VcnGatewayConnectionConfigTest.buildTestConfigWithExposedCaps( + NET_CAPABILITY_INTERNET, NET_CAPABILITY_DUN); + VcnGatewayConnection gatewayConnection = + new VcnGatewayConnection( + mVcnContext, + TEST_SUB_GRP, + TEST_SUBSCRIPTION_SNAPSHOT, + gatewayConfig, + mGatewayStatusCallback, + true /* isMobileDataEnabled */, + mDeps); + gatewayConnection.setUnderlyingNetwork(TEST_UNDERLYING_NETWORK_RECORD_1); + final VcnIkeSession session = + gatewayConnection.buildIkeSession(TEST_UNDERLYING_NETWORK_RECORD_1.network); + gatewayConnection.setIkeSession(session); + gatewayConnection.transitionTo(gatewayConnection.mConnectedState); + mTestLooper.dispatchAll(); + + verifyVcnTransformsApplied(gatewayConnection, true /* expectForwardTransform */); } @Test diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java index 284f1f88658e..1ecb4c9ee298 100644 --- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java +++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTestBase.java @@ -220,7 +220,7 @@ public class VcnGatewayConnectionTestBase { protected VcnChildSessionCallback getChildSessionCallback() { ArgumentCaptor<ChildSessionCallback> captor = ArgumentCaptor.forClass(ChildSessionCallback.class); - verify(mDeps).newIkeSession(any(), any(), any(), any(), captor.capture()); + verify(mDeps, atLeastOnce()).newIkeSession(any(), any(), any(), any(), captor.capture()); return (VcnChildSessionCallback) captor.getValue(); } |