Merge "Fix incompatibilities with Kotlin 1.5.0"
diff --git a/Android.bp b/Android.bp
index 02db27a..c5d557c 100644
--- a/Android.bp
+++ b/Android.bp
@@ -211,6 +211,7 @@
"apex_aidl_interface-java",
"framework-protos",
"updatable-driver-protos",
+ "ota_metadata_proto_java",
"android.hidl.base-V1.0-java",
"android.hardware.cas-V1.0-java",
"android.hardware.cas-V1.1-java",
@@ -434,8 +435,21 @@
"--api-lint-ignore-prefix junit. " +
"--api-lint-ignore-prefix org. "
+packages_to_document = [
+ "android",
+ "dalvik",
+ "java",
+ "javax",
+ "junit",
+ "org.apache.http",
+ "org.json",
+ "org.w3c.dom",
+ "org.xml.sax",
+ "org.xmlpull",
+]
+
filegroup {
- name: "framework-non-updatable-stub-sources",
+ name: "android-non-updatable-stub-sources",
srcs: [
":framework-mime-sources", // mimemap builds separately but has no separate droidstubs.
":framework-non-updatable-sources",
@@ -447,6 +461,61 @@
visibility: ["//visibility:private"],
}
+// These defaults are used for both the jar stubs and the doc stubs.
+stubs_defaults {
+ name: "android-non-updatable-stubs-defaults",
+ srcs: [":android-non-updatable-stub-sources"],
+ sdk_version: "none",
+ system_modules: "none",
+ java_version: "1.8",
+ arg_files: ["core/res/AndroidManifest.xml"],
+ // TODO(b/147699819): remove below aidl includes.
+ aidl: {
+ local_include_dirs: [
+ "apex/media/aidl/stable",
+ // TODO: move to include-dirs for packages/modules/Connectivity when this moves out of
+ // frameworks/base
+ "packages/Connectivity/framework/aidl-export",
+ "telephony/java",
+ ],
+ },
+ // These are libs from framework-internal-utils that are required (i.e. being referenced)
+ // from framework-non-updatable-sources. Add more here when there's a need.
+ // DO NOT add the entire framework-internal-utils. It might cause unnecessary circular
+ // dependencies gets bigger.
+ libs: [
+ "android.hardware.cas-V1.2-java",
+ "android.hardware.health-V1.0-java-constants",
+ "android.hardware.radio-V1.5-java",
+ "android.hardware.radio-V1.6-java",
+ "android.hardware.thermal-V1.0-java-constants",
+ "android.hardware.thermal-V2.0-java",
+ "android.hardware.tv.input-V1.0-java-constants",
+ "android.hardware.tv.tuner-V1.0-java-constants",
+ "android.hardware.usb-V1.0-java-constants",
+ "android.hardware.usb-V1.1-java-constants",
+ "android.hardware.usb.gadget-V1.0-java",
+ "android.hardware.vibrator-V1.3-java",
+ "framework-protos",
+ "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,
+ // the prebuilts/sdk/current needs to be updated.
+ "sdk_system_current_android",
+ // NOTE: The below can be removed once the prebuilt stub contains IKE.
+ "sdk_system_current_android.net.ipsec.ike",
+ ],
+ filter_packages: packages_to_document,
+ high_mem: true, // Lots of sources => high memory use, see b/170701554
+ installable: false,
+ annotations_enabled: true,
+ previous_api: ":android.api.public.latest",
+ merge_annotations_dirs: ["metalava-manual"],
+ defaults_visibility: ["//visibility:private"],
+ visibility: ["//frameworks/base/api"],
+}
+
build = [
"StubLibraries.bp",
"ApiDocs.bp",
diff --git a/ApiDocs.bp b/ApiDocs.bp
index 5cbc78c..83ace02 100644
--- a/ApiDocs.bp
+++ b/ApiDocs.bp
@@ -56,9 +56,23 @@
]
stubs_defaults {
+ name: "android-non-updatable-doc-stubs-defaults",
+ defaults: ["android-non-updatable-stubs-defaults"],
+ srcs: [
+ // No longer part of the stubs, but are included in the docs.
+ "test-base/src/**/*.java",
+ "test-mock/src/**/*.java",
+ "test-runner/src/**/*.java",
+ ],
+ libs: framework_docs_only_libs,
+ create_doc_stubs: true,
+ write_sdk_values: true,
+}
+
+stubs_defaults {
name: "framework-doc-stubs-default",
srcs: [
- ":framework-non-updatable-stub-sources",
+ ":android-non-updatable-stub-sources",
// Module sources
":art.module.public.api{.public.stubs.source}",
@@ -75,13 +89,14 @@
":updatable-media-srcs",
// No longer part of the stubs, but are included in the docs.
- "test-base/src/**/*.java",
- "test-mock/src/**/*.java",
- "test-runner/src/**/*.java",
+ ":android-test-base-sources",
+ ":android-test-mock-sources",
+ ":android-test-runner-sources",
],
libs: framework_docs_only_libs,
create_doc_stubs: true,
annotations_enabled: true,
+ filter_packages: packages_to_document,
api_levels_annotations_enabled: true,
api_levels_annotations_dirs: [
"sdk-dir",
@@ -94,6 +109,19 @@
}
droidstubs {
+ name: "android-non-updatable-doc-stubs",
+ defaults: ["android-non-updatable-doc-stubs-defaults"],
+ args: metalava_framework_docs_args,
+}
+
+droidstubs {
+ name: "android-non-updatable-doc-stubs-system",
+ defaults: ["android-non-updatable-doc-stubs-defaults"],
+ args: metalava_framework_docs_args +
+ " --show-annotation android.annotation.SystemApi\\(client=android.annotation.SystemApi.Client.PRIVILEGED_APPS\\) ",
+}
+
+droidstubs {
name: "framework-doc-stubs",
defaults: ["framework-doc-stubs-default"],
arg_files: [
diff --git a/StubLibraries.bp b/StubLibraries.bp
index 10d64fc..626f977 100644
--- a/StubLibraries.bp
+++ b/StubLibraries.bp
@@ -27,74 +27,11 @@
// Common metalava configs
/////////////////////////////////////////////////////////////////////
-packages_to_document = [
- "android",
- "dalvik",
- "java",
- "javax",
- "junit",
- "org.apache.http",
- "org.json",
- "org.w3c.dom",
- "org.xml.sax",
- "org.xmlpull",
-]
-
stubs_defaults {
name: "metalava-non-updatable-api-stubs-default",
- srcs: [":framework-non-updatable-stub-sources"],
- sdk_version: "none",
- system_modules: "none",
- java_version: "1.8",
- arg_files: ["core/res/AndroidManifest.xml"],
- // TODO(b/147699819): remove below aidl includes.
- aidl: {
- local_include_dirs: [
- "apex/media/aidl/stable",
- // TODO: move to include-dirs for packages/modules/Connectivity when this moves out of
- // frameworks/base
- "packages/Connectivity/framework/aidl-export",
- "telephony/java",
- ],
- },
- // These are libs from framework-internal-utils that are required (i.e. being referenced)
- // from framework-non-updatable-sources. Add more here when there's a need.
- // DO NOT add the entire framework-internal-utils. It might cause unnecessary circular
- // dependencies gets bigger.
- libs: [
- "android.hardware.cas-V1.2-java",
- "android.hardware.health-V1.0-java-constants",
- "android.hardware.radio-V1.5-java",
- "android.hardware.radio-V1.6-java",
- "android.hardware.thermal-V1.0-java-constants",
- "android.hardware.thermal-V2.0-java",
- "android.hardware.tv.input-V1.0-java-constants",
- "android.hardware.tv.tuner-V1.0-java-constants",
- "android.hardware.usb-V1.0-java-constants",
- "android.hardware.usb-V1.1-java-constants",
- "android.hardware.usb.gadget-V1.0-java",
- "android.hardware.vibrator-V1.3-java",
- "framework-protos",
- "stable.core.platform.api.stubs",
- // 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,
- // the prebuilts/sdk/current needs to be updated.
- "sdk_system_current_android",
- // NOTE: The below can be removed once the prebuilt stub contains IKE.
- "sdk_system_current_android.net.ipsec.ike",
- ],
- high_mem: true, // Lots of sources => high memory use, see b/170701554
- installable: false,
- annotations_enabled: true,
- previous_api: ":android.api.public.latest",
- merge_annotations_dirs: [
- "metalava-manual",
- ],
+ defaults: ["android-non-updatable-stubs-defaults"],
api_levels_annotations_enabled: false,
- filter_packages: packages_to_document,
defaults_visibility: ["//visibility:private"],
- visibility: ["//frameworks/base/api"],
}
/////////////////////////////////////////////////////////////////////
diff --git a/boot/Android.bp b/boot/Android.bp
index ef2abc8..3caede4 100644
--- a/boot/Android.bp
+++ b/boot/Android.bp
@@ -98,4 +98,12 @@
dest: "hiddenapi-unsupported.csv",
},
],
+
+ required: [
+ "platform-systemserverclasspath",
+ ],
+}
+
+platform_systemserverclasspath {
+ name: "platform-systemserverclasspath",
}
diff --git a/boot/OWNERS b/boot/OWNERS
index 0648888..0e258d0 100644
--- a/boot/OWNERS
+++ b/boot/OWNERS
@@ -1,2 +1,6 @@
# soong-team@ as the platform_bootclasspath module is tightly coupled with Soong
file:platform/build/soong:/OWNERS
+
+# art-team@ manages the boot image profiles for frameworks
+per-file boot-* = calin@google.com, yawanng@google.com, ngeoffray@google.com
+per-file preloaded-classes* = calin@google.com, yawanng@google.com, ngeoffray@google.com
diff --git a/boot/boot-image-profile.txt b/boot/boot-image-profile.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/boot/boot-image-profile.txt
diff --git a/boot/preloaded-classes b/boot/preloaded-classes
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/boot/preloaded-classes
diff --git a/core/api/current.txt b/core/api/current.txt
index 7fd3bca..cd4e6f9 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -31480,6 +31480,7 @@
field public static final android.print.PrintAttributes.MediaSize JPN_HAGAKI;
field public static final android.print.PrintAttributes.MediaSize JPN_KAHU;
field public static final android.print.PrintAttributes.MediaSize JPN_KAKU2;
+ field @NonNull public static final android.print.PrintAttributes.MediaSize JPN_OE_PHOTO_L;
field public static final android.print.PrintAttributes.MediaSize JPN_OUFUKU;
field public static final android.print.PrintAttributes.MediaSize JPN_YOU4;
field @NonNull public static final android.print.PrintAttributes.MediaSize NA_ARCH_A;
@@ -31501,7 +31502,6 @@
field public static final android.print.PrintAttributes.MediaSize NA_QUARTO;
field @NonNull public static final android.print.PrintAttributes.MediaSize NA_SUPER_B;
field public static final android.print.PrintAttributes.MediaSize NA_TABLOID;
- field @NonNull public static final android.print.PrintAttributes.MediaSize OE_PHOTO_L;
field public static final android.print.PrintAttributes.MediaSize OM_DAI_PA_KAI;
field public static final android.print.PrintAttributes.MediaSize OM_JUURO_KU_KAI;
field public static final android.print.PrintAttributes.MediaSize OM_PA_KAI;
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index ea626b3..bb42ddc 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -12331,6 +12331,7 @@
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/app/admin/OWNERS b/core/java/android/app/admin/OWNERS
index 8462cbe..6acbef2 100644
--- a/core/java/android/app/admin/OWNERS
+++ b/core/java/android/app/admin/OWNERS
@@ -3,9 +3,9 @@
# Android Enterprise team
rubinxu@google.com
sandness@google.com
-eranm@google.com
alexkershaw@google.com
pgrafov@google.com
# Emeritus
yamasani@google.com
+eranm@google.com
diff --git a/core/java/android/bluetooth/BluetoothDevice.java b/core/java/android/bluetooth/BluetoothDevice.java
index a40bf34..11b45e3 100644
--- a/core/java/android/bluetooth/BluetoothDevice.java
+++ b/core/java/android/bluetooth/BluetoothDevice.java
@@ -1605,13 +1605,13 @@
*
* <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 98acd98..01d1aa5 100644
--- a/core/java/android/net/IpSecManager.java
+++ b/core/java/android/net/IpSecManager.java
@@ -79,6 +79,16 @@
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 5e93820..14e70cf 100644
--- a/core/java/android/net/vcn/VcnNetworkPolicyResult.java
+++ b/core/java/android/net/vcn/VcnNetworkPolicyResult.java
@@ -87,6 +87,16 @@
&& 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 b47d564..b0d4f3b 100644
--- a/core/java/android/net/vcn/VcnUnderlyingNetworkPolicy.java
+++ b/core/java/android/net/vcn/VcnUnderlyingNetworkPolicy.java
@@ -85,6 +85,11 @@
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 e47ffcc..5017d9e 100755
--- a/core/java/android/os/Build.java
+++ b/core/java/android/os/Build.java
@@ -424,7 +424,8 @@
* 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 @@
* 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 1e60f74..a7516a4 100644
--- a/core/java/android/os/Debug.java
+++ b/core/java/android/os/Debug.java
@@ -74,8 +74,9 @@
*
* @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/android/os/IRecoverySystem.aidl b/core/java/android/os/IRecoverySystem.aidl
index 9368b68..88bdb7f 100644
--- a/core/java/android/os/IRecoverySystem.aidl
+++ b/core/java/android/os/IRecoverySystem.aidl
@@ -23,6 +23,7 @@
/** @hide */
interface IRecoverySystem {
+ boolean allocateSpaceForUpdate(in String packageFilePath);
boolean uncrypt(in String packageFile, IRecoverySystemProgressListener listener);
boolean setupBcb(in String command);
boolean clearBcb();
diff --git a/core/java/android/os/RecoverySystem.java b/core/java/android/os/RecoverySystem.java
index 051447f..944b717 100644
--- a/core/java/android/os/RecoverySystem.java
+++ b/core/java/android/os/RecoverySystem.java
@@ -672,6 +672,14 @@
if (!rs.setupBcb(command)) {
throw new IOException("Setup BCB failed");
}
+ try {
+ if (!rs.allocateSpaceForUpdate(packageFile)) {
+ throw new IOException("Failed to allocate space for update "
+ + packageFile.getAbsolutePath());
+ }
+ } catch (RemoteException e) {
+ e.rethrowAsRuntimeException();
+ }
// Having set up the BCB (bootloader control block), go ahead and reboot
PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
@@ -1392,6 +1400,13 @@
}
/**
+ * Talks to RecoverySystemService via Binder to allocate space
+ */
+ private boolean allocateSpaceForUpdate(File packageFile) throws RemoteException {
+ return mService.allocateSpaceForUpdate(packageFile.getAbsolutePath());
+ }
+
+ /**
* Talks to RecoverySystemService via Binder to clear up the BCB.
*/
private boolean clearBcb() {
diff --git a/core/java/android/print/PrintAttributes.java b/core/java/android/print/PrintAttributes.java
index c198848..f508101 100644
--- a/core/java/android/print/PrintAttributes.java
+++ b/core/java/android/print/PrintAttributes.java
@@ -837,8 +837,8 @@
new MediaSize("JPN_YOU4", "android",
R.string.mediasize_japanese_you4, 4134, 9252);
/** Japanese Photo L media size: 89mm x 127mm (3.5 x 5") */
- public static final @NonNull MediaSize OE_PHOTO_L =
- new MediaSize("OE_PHOTO_L", "android",
+ public static final @NonNull MediaSize JPN_OE_PHOTO_L =
+ new MediaSize("JPN_OE_PHOTO_L", "android",
R.string.mediasize_japanese_l, 3500, 5000);
private final @NonNull String mId;
diff --git a/core/java/android/util/JsonReader.java b/core/java/android/util/JsonReader.java
index 50f63f8..c75e238 100644
--- a/core/java/android/util/JsonReader.java
+++ b/core/java/android/util/JsonReader.java
@@ -16,7 +16,7 @@
package android.util;
-import libcore.internal.StringPool;
+import com.android.internal.util.StringPool;
import java.io.Closeable;
import java.io.EOFException;
diff --git a/core/java/com/android/internal/compat/CompatibilityChangeConfig.java b/core/java/com/android/internal/compat/CompatibilityChangeConfig.java
index 36bc229..182dba7 100644
--- a/core/java/com/android/internal/compat/CompatibilityChangeConfig.java
+++ b/core/java/com/android/internal/compat/CompatibilityChangeConfig.java
@@ -39,14 +39,14 @@
* Changes forced to be enabled.
*/
public Set<Long> enabledChanges() {
- return mChangeConfig.forceEnabledSet();
+ return mChangeConfig.getEnabledSet();
}
/**
* Changes forced to be disabled.
*/
public Set<Long> disabledChanges() {
- return mChangeConfig.forceDisabledSet();
+ return mChangeConfig.getDisabledSet();
}
/**
@@ -84,8 +84,8 @@
@Override
public void writeToParcel(Parcel dest, int flags) {
- long[] enabled = mChangeConfig.forceEnabledChangesArray();
- long[] disabled = mChangeConfig.forceDisabledChangesArray();
+ long[] enabled = mChangeConfig.getEnabledChangesArray();
+ long[] disabled = mChangeConfig.getDisabledChangesArray();
dest.writeLongArray(enabled);
dest.writeLongArray(disabled);
diff --git a/core/java/com/android/internal/os/BinderCallsStats.java b/core/java/com/android/internal/os/BinderCallsStats.java
index 0fb2728..dbba469 100644
--- a/core/java/com/android/internal/os/BinderCallsStats.java
+++ b/core/java/com/android/internal/os/BinderCallsStats.java
@@ -54,6 +54,7 @@
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 @@
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 @@
if (shouldRecordDetailedData()) {
s.cpuTimeStarted = getThreadTimeMicro();
s.timeStarted = getElapsedRealtimeMicro();
+ } else if (mCollectLatencyData) {
+ s.timeStarted = getElapsedRealtimeMicro();
}
+
return s;
}
@@ -153,6 +165,10 @@
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 @@
}
}
+ /** 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 @@
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 @@
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 0000000..92b4952
--- /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/java/com/android/internal/util/StringPool.java b/core/java/com/android/internal/util/StringPool.java
new file mode 100644
index 0000000..c5180a3
--- /dev/null
+++ b/core/java/com/android/internal/util/StringPool.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.util;
+
+/**
+ * A pool of string instances. Unlike the {@link String#intern() VM's
+ * interned strings}, this pool provides no guarantee of reference equality.
+ * It is intended only to save allocations. This class is not thread safe.
+ *
+ * @hide
+ */
+public final class StringPool {
+
+ private final String[] mPool = new String[512];
+
+ /**
+ * Constructs string pool.
+ */
+ public StringPool() {
+ }
+
+ private static boolean contentEquals(String s, char[] chars, int start, int length) {
+ if (s.length() != length) {
+ return false;
+ }
+ for (int i = 0; i < length; i++) {
+ if (chars[start + i] != s.charAt(i)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Returns a string equal to {@code new String(array, start, length)}.
+ *
+ * @param array buffer containing string chars
+ * @param start offset in {@code array} where string starts
+ * @param length length of string
+ * @return string equal to {@code new String(array, start, length)}
+ */
+ public String get(char[] array, int start, int length) {
+ // Compute an arbitrary hash of the content
+ int hashCode = 0;
+ for (int i = start; i < start + length; i++) {
+ hashCode = (hashCode * 31) + array[i];
+ }
+
+ // Pick a bucket using Doug Lea's supplemental secondaryHash function (from HashMap)
+ hashCode ^= (hashCode >>> 20) ^ (hashCode >>> 12);
+ hashCode ^= (hashCode >>> 7) ^ (hashCode >>> 4);
+ int index = hashCode & (mPool.length - 1);
+
+ String pooled = mPool[index];
+ if (pooled != null && contentEquals(pooled, array, start, length)) {
+ return pooled;
+ }
+
+ String result = new String(array, start, length);
+ mPool[index] = result;
+ return result;
+ }
+}
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index 855448b..114f395 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -630,6 +630,7 @@
char hotstartupsamplesOptsBuf[sizeof("-Xps-hot-startup-method-samples:")-1 + PROPERTY_VALUE_MAX];
char saveResolvedClassesDelayMsOptsBuf[
sizeof("-Xps-save-resolved-classes-delay-ms:")-1 + PROPERTY_VALUE_MAX];
+ char profileMinSavePeriodOptsBuf[sizeof("-Xps-min-save-period-ms:")-1 + PROPERTY_VALUE_MAX];
char madviseRandomOptsBuf[sizeof("-XX:MadviseRandomAccess:")-1 + PROPERTY_VALUE_MAX];
char madviseWillNeedFileSizeVdex[
sizeof("-XMadviseWillNeedVdexFileSize:")-1 + PROPERTY_VALUE_MAX];
@@ -662,6 +663,8 @@
char extraOptsBuf[PROPERTY_VALUE_MAX];
char voldDecryptBuf[PROPERTY_VALUE_MAX];
char perfettoHprofOptBuf[sizeof("-XX:PerfettoHprof=") + PROPERTY_VALUE_MAX];
+ char perfettoJavaHeapStackOptBuf[
+ sizeof("-XX:PerfettoJavaHeapStackProf=") + PROPERTY_VALUE_MAX];
enum {
kEMDefault,
kEMIntPortable,
@@ -776,6 +779,10 @@
parseRuntimeOption("dalvik.vm.perfetto_hprof", perfettoHprofOptBuf, "-XX:PerfettoHprof=",
"true");
+ // Enable PerfettoJavaHeapStackProf in the zygote
+ parseRuntimeOption("dalvik.vm.perfetto_javaheap", perfettoJavaHeapStackOptBuf,
+ "-XX:PerfettoJavaHeapStackProf=", "true");
+
if (primary_zygote) {
addOption("-Xprimaryzygote");
}
@@ -859,6 +866,9 @@
parseRuntimeOption("dalvik.vm.ps-resolved-classes-delay-ms", saveResolvedClassesDelayMsOptsBuf,
"-Xps-save-resolved-classes-delay-ms:");
+ parseRuntimeOption("dalvik.vm.ps-min-save-period-ms", profileMinSavePeriodOptsBuf,
+ "-Xps-min-save-period-ms:");
+
property_get("ro.config.low_ram", propBuf, "");
if (strcmp(propBuf, "true") == 0) {
addOption("-XX:LowMemoryMode");
diff --git a/core/jni/android_media_AudioSystem.cpp b/core/jni/android_media_AudioSystem.cpp
index feb8930..1a7f6e8 100644
--- a/core/jni/android_media_AudioSystem.cpp
+++ b/core/jni/android_media_AudioSystem.cpp
@@ -2273,10 +2273,19 @@
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 @@
(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/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index cbc08ba..6bdfe28 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -632,6 +632,13 @@
<!-- The default minimal size of a PiP task, in both dimensions. -->
<dimen name="default_minimal_size_pip_resizable_task">108dp</dimen>
+ <!--
+ The overridable minimal size of a PiP task, in both dimensions.
+ Different from default_minimal_size_pip_resizable_task, this is to limit the dimension
+ when the pinned stack size is overridden by app via minWidth/minHeight.
+ -->
+ <dimen name="overridable_minimal_size_pip_resizable_task">48dp</dimen>
+
<!-- Height of a task when in minimized mode from the top when launcher is resizable. -->
<dimen name="task_height_of_minimized_mode">80dp</dimen>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index c06e7de..43f4300 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -1941,6 +1941,7 @@
<java-symbol type="fraction" name="config_dimBehindFadeDuration" />
<java-symbol type="dimen" name="default_minimal_size_resizable_task" />
<java-symbol type="dimen" name="default_minimal_size_pip_resizable_task" />
+ <java-symbol type="dimen" name="overridable_minimal_size_pip_resizable_task" />
<java-symbol type="dimen" name="task_height_of_minimized_mode" />
<java-symbol type="fraction" name="config_screenAutoBrightnessDozeScaleFactor" />
<java-symbol type="bool" name="config_allowPriorityVibrationsInLowPowerMode" />
diff --git a/core/tests/bandwidthtests/src/com/android/bandwidthtest/util/ConnectionUtil.java b/core/tests/bandwidthtests/src/com/android/bandwidthtest/util/ConnectionUtil.java
index dd60dd4..1a63660 100644
--- a/core/tests/bandwidthtests/src/com/android/bandwidthtest/util/ConnectionUtil.java
+++ b/core/tests/bandwidthtests/src/com/android/bandwidthtest/util/ConnectionUtil.java
@@ -44,8 +44,6 @@
import com.android.bandwidthtest.NetworkState.StateTransitionDirection;
import com.android.internal.util.AsyncChannel;
-import junit.framework.Assert;
-
import java.io.IOException;
import java.net.UnknownHostException;
import java.util.List;
@@ -76,7 +74,11 @@
private WifiManager mWifiManager;
private Context mContext;
// Verify connectivity state
- private static final int NUM_NETWORK_TYPES = ConnectivityManager.MAX_NETWORK_TYPE + 1;
+ // ConnectivityManager.TYPE_* is deprecated and no longer extended, so use the max public
+ // network type - TYPE_VPN should be enough.
+ // TODO: Replace registering CONNECTIVITY_ACTION with registering NetworkCallback and check
+ // network by NetworkCapabilities.TRANSPORT_* and NetworkCapabilities.hasTransport() instead.
+ private static final int NUM_NETWORK_TYPES = ConnectivityManager.TYPE_VPN + 1;
private NetworkState[] mConnectivityState = new NetworkState[NUM_NETWORK_TYPES];
public ConnectionUtil(Context context) {
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 eb9c7b6..1156120 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 @@
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 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 0000000..36915a2
--- /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/core/tests/utiltests/src/com/android/internal/util/StringPoolTest.java b/core/tests/utiltests/src/com/android/internal/util/StringPoolTest.java
new file mode 100644
index 0000000..f67fd51
--- /dev/null
+++ b/core/tests/utiltests/src/com/android/internal/util/StringPoolTest.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.util;
+
+import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.SmallTest;
+
+@SmallTest
+public final class StringPoolTest extends AndroidTestCase {
+
+ public void testStringPool() {
+ StringPool stringPool = new StringPool();
+ String bcd = stringPool.get(new char[] { 'a', 'b', 'c', 'd', 'e' }, 1, 3);
+ assertEquals("bcd", bcd);
+ assertSame(bcd, stringPool.get(new char[] { 'a', 'b', 'c', 'd', 'e' }, 1, 3));
+ }
+
+ public void testHashCollision() {
+ StringPool stringPool = new StringPool();
+ char[] a = { (char) 1, (char) 0 };
+ char[] b = { (char) 0, (char) 31 };
+ assertEquals(new String(a).hashCode(), new String(b).hashCode());
+
+ String aString = stringPool.get(a, 0, 2);
+ assertEquals(new String(a), aString);
+ String bString = stringPool.get(b, 0, 2);
+ assertEquals(new String(b), bString);
+ assertSame(bString, stringPool.get(b, 0, 2));
+ assertNotSame(aString, stringPool.get(a, 0, 2));
+ }
+}
diff --git a/keystore/java/android/security/keystore/AttestationUtils.java b/keystore/java/android/security/keystore/AttestationUtils.java
index be865a0..3980d3a 100644
--- a/keystore/java/android/security/keystore/AttestationUtils.java
+++ b/keystore/java/android/security/keystore/AttestationUtils.java
@@ -293,6 +293,11 @@
} catch (SecurityException e) {
throw e;
} catch (Exception e) {
+ // If a DeviceIdAttestationException was previously wrapped with some other type,
+ // let's throw the original exception instead of wrapping it yet again.
+ if (e.getCause() instanceof DeviceIdAttestationException) {
+ throw (DeviceIdAttestationException) e.getCause();
+ }
throw new DeviceIdAttestationException("Unable to perform attestation", e);
}
}
diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp
index 6220abe..e74d3966a 100644
--- a/libs/hwui/Android.bp
+++ b/libs/hwui/Android.bp
@@ -528,6 +528,7 @@
"libhwui_defaults",
"android_graphics_apex",
"android_graphics_jni",
+ "linker_hugepage_aligned",
],
export_header_lib_headers: ["android_graphics_apex_headers"],
}
diff --git a/location/java/com/android/internal/location/GpsNetInitiatedHandler.java b/location/java/com/android/internal/location/GpsNetInitiatedHandler.java
index 139474c..67a040d 100644
--- a/location/java/com/android/internal/location/GpsNetInitiatedHandler.java
+++ b/location/java/com/android/internal/location/GpsNetInitiatedHandler.java
@@ -28,6 +28,7 @@
import android.os.RemoteException;
import android.os.SystemClock;
import android.os.UserHandle;
+import android.telephony.PhoneNumberUtils;
import android.telephony.PhoneStateListener;
import android.telephony.TelephonyManager;
import android.util.Log;
@@ -160,7 +161,7 @@
be set to true when the phone is having emergency call, and then will
be set to false by mPhoneStateListener when the emergency call ends.
*/
- mIsInEmergencyCall = mTelephonyManager.isEmergencyNumber(phoneNumber);
+ mIsInEmergencyCall = PhoneNumberUtils.isEmergencyNumber(phoneNumber);
if (DEBUG) Log.v(TAG, "ACTION_NEW_OUTGOING_CALL - " + getInEmergency());
} else if (action.equals(LocationManager.MODE_CHANGED_ACTION)) {
updateLocationMode();
diff --git a/media/java/android/media/AudioFormat.java b/media/java/android/media/AudioFormat.java
index bd2524f..5885c7d 100644
--- a/media/java/android/media/AudioFormat.java
+++ b/media/java/android/media/AudioFormat.java
@@ -518,13 +518,13 @@
* @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 8425185..2d9bdaf 100644
--- a/media/java/android/media/AudioSystem.java
+++ b/media/java/android/media/AudioSystem.java
@@ -101,11 +101,31 @@
*/
public static final int NUM_STREAMS = 5;
- /** Maximum value for AudioTrack channel count
- * @hide public for MediaCode only, do not un-hide or change to a numeric literal
+ /*
+ * 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.
*/
- public static final int OUT_CHANNEL_COUNT_MAX = native_get_FCC_8();
- private static native int native_get_FCC_8();
+
+ /** Maximum value for AudioTrack channel count
+ * @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 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/Connectivity/framework/src/android/net/NetworkCapabilities.java b/packages/Connectivity/framework/src/android/net/NetworkCapabilities.java
index 937a9d2..950b1c7 100644
--- a/packages/Connectivity/framework/src/android/net/NetworkCapabilities.java
+++ b/packages/Connectivity/framework/src/android/net/NetworkCapabilities.java
@@ -2444,7 +2444,8 @@
* For example {@code TRANSPORT_WIFI} and {@code TRANSPORT_ETHERNET} added to a
* {@code NetworkCapabilities} would cause either a Wi-Fi network or an Ethernet network
* to be selected. This is logically different than
- * {@code NetworkCapabilities.NET_CAPABILITY_*}.
+ * {@code NetworkCapabilities.NET_CAPABILITY_*}. Also note that multiple networks with the
+ * same transport type may be active concurrently.
*
* @param transportType the transport type to be added or removed.
* @return this builder
diff --git a/packages/SettingsLib/src/com/android/settingslib/net/DataUsageController.java b/packages/SettingsLib/src/com/android/settingslib/net/DataUsageController.java
index 60bcf37..18c38c5 100644
--- a/packages/SettingsLib/src/com/android/settingslib/net/DataUsageController.java
+++ b/packages/SettingsLib/src/com/android/settingslib/net/DataUsageController.java
@@ -113,7 +113,8 @@
}
public DataUsageInfo getWifiDataUsageInfo() {
- NetworkTemplate template = NetworkTemplate.buildTemplateWifiWildcard();
+ NetworkTemplate template = NetworkTemplate.buildTemplateWifi(
+ NetworkTemplate.WIFI_NETWORKID_ALL, null);
return getDataUsageInfo(template);
}
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 bcabec8..2c1d3e2 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 @@
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 @@
.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 @@
.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 @@
.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 @@
.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/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/DataUsageControllerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/DataUsageControllerTest.java
index f7bee30..27d877d 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/DataUsageControllerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/DataUsageControllerTest.java
@@ -89,7 +89,8 @@
mNetworkTemplate = NetworkTemplate.buildTemplateMobileAll(SUB_ID);
mNetworkTemplate2 = NetworkTemplate.buildTemplateMobileAll(SUB_ID_2);
- mWifiNetworkTemplate = NetworkTemplate.buildTemplateWifiWildcard();
+ mWifiNetworkTemplate = NetworkTemplate.buildTemplateWifi(
+ NetworkTemplate.WIFI_NETWORKID_ALL, null);
}
@Test
diff --git a/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java b/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java
index 54df53d..29d77a7 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java
@@ -45,6 +45,7 @@
import android.os.IBinder;
import android.os.Looper;
import android.os.RemoteException;
+import android.util.EventLog;
import android.util.Log;
import android.util.Size;
import android.view.SurfaceControl;
@@ -223,6 +224,7 @@
private PipSurfaceTransactionHelper.SurfaceControlTransactionFactory
mSurfaceControlTransactionFactory;
private PictureInPictureParams mPictureInPictureParams;
+ private int mOverridableMinSize;
/**
* If set to {@code true}, the entering animation will be skipped and we will wait for
@@ -244,6 +246,8 @@
mPipBoundsHandler = boundsHandler;
mEnterExitAnimationDuration = context.getResources()
.getInteger(R.integer.config_pipResizeAnimationDuration);
+ mOverridableMinSize = context.getResources().getDimensionPixelSize(
+ com.android.internal.R.dimen.overridable_minimal_size_pip_resizable_task);
mSurfaceTransactionHelper = surfaceTransactionHelper;
mPipAnimationController = pipAnimationController;
mPipUiEventLoggerLogger = pipUiEventLogger;
@@ -949,7 +953,14 @@
// -1 will be populated if an activity specifies defaultWidth/defaultHeight in <layout>
// without minWidth/minHeight
if (windowLayout.minWidth > 0 && windowLayout.minHeight > 0) {
- return new Size(windowLayout.minWidth, windowLayout.minHeight);
+ // If either dimension is smaller than the allowed minimum, adjust them
+ // according to mOverridableMinSize and log to SafeNet
+ if (windowLayout.minWidth < mOverridableMinSize
+ || windowLayout.minHeight < mOverridableMinSize) {
+ EventLog.writeEvent(0x534e4554, "174302616", -1, "");
+ }
+ return new Size(Math.max(windowLayout.minWidth, mOverridableMinSize),
+ Math.max(windowLayout.minHeight, mOverridableMinSize));
}
return null;
}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotNotificationsController.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotNotificationsController.java
index 42dde40..b8a468e 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotNotificationsController.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotNotificationsController.java
@@ -252,7 +252,7 @@
dpm.createAdminSupportIntent(DevicePolicyManager.POLICY_DISABLE_SCREEN_CAPTURE);
if (intent != null) {
final PendingIntent pendingIntent = PendingIntent.getActivityAsUser(
- mContext, 0, intent, 0, null, UserHandle.CURRENT);
+ mContext, 0, intent, PendingIntent.FLAG_IMMUTABLE, null, UserHandle.CURRENT);
b.setContentIntent(pendingIntent);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotControllerImpl.java
index 60ee75b..f6f8d6c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HotspotControllerImpl.java
@@ -193,7 +193,8 @@
if (enabled) {
mWaitingForTerminalState = true;
if (DEBUG) Log.d(TAG, "Starting tethering");
- mTetheringManager.startTethering(new TetheringRequest.Builder(TETHERING_WIFI).build(),
+ mTetheringManager.startTethering(new TetheringRequest.Builder(
+ TETHERING_WIFI).setShouldShowEntitlementUi(false).build(),
ConcurrentUtils.DIRECT_EXECUTOR,
new TetheringManager.StartTetheringCallback() {
@Override
diff --git a/services/Android.bp b/services/Android.bp
index 57a7bc5..1f4258d 100644
--- a/services/Android.bp
+++ b/services/Android.bp
@@ -156,13 +156,13 @@
defaults: ["services-stubs-default"],
check_api: {
current: {
- api_file: "api/non-updatable-current.txt",
- removed_api_file: "api/non-updatable-removed.txt",
+ api_file: "api/current.txt",
+ removed_api_file: "api/removed.txt",
},
api_lint: {
enabled: true,
new_since: ":android-non-updatable.api.system-server.latest",
- baseline_file: "api/non-updatable-lint-baseline.txt",
+ baseline_file: "api/lint-baseline.txt",
},
},
dists: [
diff --git a/services/api/Android.bp b/services/api/Android.bp
index bbc8c72..ee7d49f 100644
--- a/services/api/Android.bp
+++ b/services/api/Android.bp
@@ -24,12 +24,12 @@
filegroup {
name: "non-updatable-system-server-current.txt",
- srcs: ["non-updatable-current.txt"],
+ srcs: ["current.txt"],
visibility: ["//frameworks/base/api"],
}
filegroup {
name: "non-updatable-system-server-removed.txt",
- srcs: ["non-updatable-removed.txt"],
+ srcs: ["removed.txt"],
visibility: ["//frameworks/base/api"],
}
diff --git a/services/api/current.txt b/services/api/current.txt
index 7c5c01e..6419b70 100644
--- a/services/api/current.txt
+++ b/services/api/current.txt
@@ -1,49 +1,4 @@
// Signature format: 2.0
-package com.android.permission.persistence {
-
- public interface RuntimePermissionsPersistence {
- method @NonNull public static com.android.permission.persistence.RuntimePermissionsPersistence createInstance();
- method public void deleteForUser(@NonNull android.os.UserHandle);
- method @Nullable public com.android.permission.persistence.RuntimePermissionsState readForUser(@NonNull android.os.UserHandle);
- method public void writeForUser(@NonNull com.android.permission.persistence.RuntimePermissionsState, @NonNull android.os.UserHandle);
- }
-
- public final class RuntimePermissionsState {
- ctor public RuntimePermissionsState(int, @Nullable String, @NonNull java.util.Map<java.lang.String,java.util.List<com.android.permission.persistence.RuntimePermissionsState.PermissionState>>, @NonNull java.util.Map<java.lang.String,java.util.List<com.android.permission.persistence.RuntimePermissionsState.PermissionState>>);
- method @Nullable public String getFingerprint();
- method @NonNull public java.util.Map<java.lang.String,java.util.List<com.android.permission.persistence.RuntimePermissionsState.PermissionState>> getPackagePermissions();
- method @NonNull public java.util.Map<java.lang.String,java.util.List<com.android.permission.persistence.RuntimePermissionsState.PermissionState>> getSharedUserPermissions();
- method public int getVersion();
- field public static final int NO_VERSION = -1; // 0xffffffff
- }
-
- public static final class RuntimePermissionsState.PermissionState {
- ctor public RuntimePermissionsState.PermissionState(@NonNull String, boolean, int);
- method public int getFlags();
- method @NonNull public String getName();
- method public boolean isGranted();
- }
-
-}
-
-package com.android.role.persistence {
-
- public interface RolesPersistence {
- method @NonNull public static com.android.role.persistence.RolesPersistence createInstance();
- method public void deleteForUser(@NonNull android.os.UserHandle);
- method @Nullable public com.android.role.persistence.RolesState readForUser(@NonNull android.os.UserHandle);
- method public void writeForUser(@NonNull com.android.role.persistence.RolesState, @NonNull android.os.UserHandle);
- }
-
- public final class RolesState {
- ctor public RolesState(int, @Nullable String, @NonNull java.util.Map<java.lang.String,java.util.Set<java.lang.String>>);
- method @Nullable public String getPackagesHash();
- method @NonNull public java.util.Map<java.lang.String,java.util.Set<java.lang.String>> getRoles();
- method public int getVersion();
- }
-
-}
-
package com.android.server {
public final class LocalManagerRegistry {
diff --git a/services/api/lint-baseline.txt b/services/api/lint-baseline.txt
index e985ddb..b46d21e 100644
--- a/services/api/lint-baseline.txt
+++ b/services/api/lint-baseline.txt
@@ -1,4 +1,8 @@
// Baseline format: 1.0
+NotCloseable: com.android.server.wifi.SupplicantManager:
+ Classes that release resources (stop()) should implement AutoClosable and CloseGuard: class com.android.server.wifi.SupplicantManager
+
+
ProtectedMember: com.android.server.SystemService#publishBinderService(String, android.os.IBinder):
Protected methods not allowed; must be public: method com.android.server.SystemService.publishBinderService(String,android.os.IBinder)}
ProtectedMember: com.android.server.SystemService#publishBinderService(String, android.os.IBinder, boolean):
diff --git a/services/api/non-updatable-current.txt b/services/api/non-updatable-current.txt
deleted file mode 100644
index 6419b70..0000000
--- a/services/api/non-updatable-current.txt
+++ /dev/null
@@ -1,54 +0,0 @@
-// Signature format: 2.0
-package com.android.server {
-
- public final class LocalManagerRegistry {
- method public static <T> void addManager(@NonNull Class<T>, @NonNull T);
- method @Nullable public static <T> T getManager(@NonNull Class<T>);
- }
-
- public abstract class SystemService {
- ctor public SystemService(@NonNull android.content.Context);
- method @NonNull public final android.content.Context getContext();
- method public boolean isUserSupported(@NonNull com.android.server.SystemService.TargetUser);
- method public void onBootPhase(int);
- method public abstract void onStart();
- method public void onUserStarting(@NonNull com.android.server.SystemService.TargetUser);
- method public void onUserStopped(@NonNull com.android.server.SystemService.TargetUser);
- method public void onUserStopping(@NonNull com.android.server.SystemService.TargetUser);
- method public void onUserSwitching(@Nullable com.android.server.SystemService.TargetUser, @NonNull com.android.server.SystemService.TargetUser);
- method public void onUserUnlocked(@NonNull com.android.server.SystemService.TargetUser);
- method public void onUserUnlocking(@NonNull com.android.server.SystemService.TargetUser);
- method protected final void publishBinderService(@NonNull String, @NonNull android.os.IBinder);
- method protected final void publishBinderService(@NonNull String, @NonNull android.os.IBinder, boolean);
- field public static final int PHASE_ACTIVITY_MANAGER_READY = 550; // 0x226
- field public static final int PHASE_BOOT_COMPLETED = 1000; // 0x3e8
- field public static final int PHASE_DEVICE_SPECIFIC_SERVICES_READY = 520; // 0x208
- field public static final int PHASE_LOCK_SETTINGS_READY = 480; // 0x1e0
- field public static final int PHASE_SYSTEM_SERVICES_READY = 500; // 0x1f4
- field public static final int PHASE_THIRD_PARTY_APPS_CAN_START = 600; // 0x258
- field public static final int PHASE_WAIT_FOR_DEFAULT_DISPLAY = 100; // 0x64
- }
-
- public static final class SystemService.TargetUser {
- method @NonNull public android.os.UserHandle getUserHandle();
- }
-
-}
-
-package com.android.server.stats {
-
- public final class StatsHelper {
- method public static void sendStatsdReadyBroadcast(@NonNull android.content.Context);
- }
-
-}
-
-package com.android.server.wifi {
-
- public class SupplicantManager {
- method public static void start();
- method public static void stop();
- }
-
-}
-
diff --git a/services/api/non-updatable-lint-baseline.txt b/services/api/non-updatable-lint-baseline.txt
deleted file mode 100644
index b46d21e..0000000
--- a/services/api/non-updatable-lint-baseline.txt
+++ /dev/null
@@ -1,9 +0,0 @@
-// Baseline format: 1.0
-NotCloseable: com.android.server.wifi.SupplicantManager:
- Classes that release resources (stop()) should implement AutoClosable and CloseGuard: class com.android.server.wifi.SupplicantManager
-
-
-ProtectedMember: com.android.server.SystemService#publishBinderService(String, android.os.IBinder):
- Protected methods not allowed; must be public: method com.android.server.SystemService.publishBinderService(String,android.os.IBinder)}
-ProtectedMember: com.android.server.SystemService#publishBinderService(String, android.os.IBinder, boolean):
- Protected methods not allowed; must be public: method com.android.server.SystemService.publishBinderService(String,android.os.IBinder,boolean)}
diff --git a/services/api/non-updatable-removed.txt b/services/api/non-updatable-removed.txt
deleted file mode 100644
index d802177..0000000
--- a/services/api/non-updatable-removed.txt
+++ /dev/null
@@ -1 +0,0 @@
-// Signature format: 2.0
diff --git a/services/core/java/com/android/server/BinderCallsStatsService.java b/services/core/java/com/android/server/BinderCallsStatsService.java
index c771eb7..9c8582f7 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.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 @@
/** 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 @@
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 @@
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 @@
mEnabled = enabled;
mBinderCallsStats.reset();
mBinderCallsStats.setAddDebugEntries(enabled);
+ mBinderCallsStats.getLatencyObserver().reset();
}
}
}
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index a81860d..b31adac 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -4769,6 +4769,7 @@
(Pair<ProfileNetworkPreferences.Preference, IOnCompleteListener>)
msg.obj;
handleSetProfileNetworkPreference(arg.first, arg.second);
+ break;
}
case EVENT_REPORT_NETWORK_ACTIVITY:
mNetworkActivityTracker.handleReportNetworkActivity();
diff --git a/services/core/java/com/android/server/IpSecService.java b/services/core/java/com/android/server/IpSecService.java
index 794cb93..d6ee951 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.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.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 @@
/** 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 @@
* 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 MAINLINE_NETWORK_STACK allowed to use forward policies
+ PermissionUtils.enforceNetworkStackPermission(mContext);
+ return;
}
throw new IllegalArgumentException("Invalid Direction: " + direction);
}
@@ -1353,6 +1358,26 @@
ikey,
0xffffffff,
resourceId);
+
+ // Add a forwarding policy on the tunnel interface. In order to support forwarding
+ // the IpSecTunnelInterface must have a forwarding policy matching the incoming SA.
+ //
+ // Unless a IpSecTransform is also applied against this interface in DIRECTION_FWD,
+ // forwarding will be blocked by default (as would be the case if this policy was
+ // absent).
+ //
+ // This is necessary only on the tunnel interface, and not any the interface to
+ // which traffic will be forwarded to.
+ netd.ipSecAddSecurityPolicy(
+ callerUid,
+ selAddrFamily,
+ IpSecManager.DIRECTION_FWD,
+ remoteAddr,
+ localAddr,
+ 0,
+ ikey,
+ 0xffffffff,
+ resourceId);
}
userRecord.mTunnelInterfaceRecords.put(
@@ -1820,7 +1845,7 @@
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 55e1b46..4ab947d 100644
--- a/services/core/java/com/android/server/OWNERS
+++ b/services/core/java/com/android/server/OWNERS
@@ -32,8 +32,9 @@
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/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index d9ecdda..8084de8 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -2873,6 +2873,8 @@
// If we're enforcing fine starting in Q, we also want to enforce coarse even for
// older SDK versions.
locationQueryBuilder.setMinSdkVersionForCoarse(0);
+ locationQueryBuilder.setMinSdkVersionForCoarse(0);
+ locationQueryBuilder.setMinSdkVersionForEnforcement(0);
shouldCheckLocationPermissions = true;
}
@@ -3001,6 +3003,14 @@
}
}
+ private boolean checkFineLocationAccess(Record r) {
+ return checkFineLocationAccess(r, Build.VERSION_CODES.BASE);
+ }
+
+ private boolean checkCoarseLocationAccess(Record r) {
+ return checkCoarseLocationAccess(r, Build.VERSION_CODES.BASE);
+ }
+
/**
* Note -- this method should only be used at the site of a permission check if you need to
* explicitly allow apps below a certain SDK level access regardless of location permissions.
@@ -3016,6 +3026,8 @@
.setMethod("TelephonyRegistry push")
.setLogAsInfo(true) // we don't need to log an error every time we push
.setMinSdkVersionForFine(minSdk)
+ .setMinSdkVersionForCoarse(minSdk)
+ .setMinSdkVersionForEnforcement(minSdk)
.build();
return Binder.withCleanCallingIdentity(() -> {
@@ -3040,6 +3052,8 @@
.setMethod("TelephonyRegistry push")
.setLogAsInfo(true) // we don't need to log an error every time we push
.setMinSdkVersionForCoarse(minSdk)
+ .setMinSdkVersionForFine(Integer.MAX_VALUE)
+ .setMinSdkVersionForEnforcement(minSdk)
.build();
return Binder.withCleanCallingIdentity(() -> {
diff --git a/services/core/java/com/android/server/VcnManagementService.java b/services/core/java/com/android/server/VcnManagementService.java
index 45eb774..1fd637a 100644
--- a/services/core/java/com/android/server/VcnManagementService.java
+++ b/services/core/java/com/android/server/VcnManagementService.java
@@ -87,6 +87,7 @@
import java.util.Map;
import java.util.Map.Entry;
import java.util.Objects;
+import java.util.Set;
import java.util.concurrent.TimeUnit;
/**
@@ -431,7 +432,9 @@
public void onNewSnapshot(@NonNull TelephonySubscriptionSnapshot snapshot) {
// Startup VCN instances
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()) {
@@ -478,11 +481,29 @@
entry.getValue().updateSubscriptionSnapshot(mLastSnapshot);
}
}
+
+ final Map<ParcelUuid, Set<Integer>> oldSubGrpMappings =
+ getSubGroupToSubIdMappings(oldSnapshot);
+ final Map<ParcelUuid, Set<Integer>> currSubGrpMappings =
+ getSubGroupToSubIdMappings(mLastSnapshot);
+ if (!currSubGrpMappings.equals(oldSubGrpMappings)) {
+ notifyAllPolicyListenersLocked();
+ }
}
}
}
@GuardedBy("mLock")
+ private Map<ParcelUuid, Set<Integer>> getSubGroupToSubIdMappings(
+ @NonNull TelephonySubscriptionSnapshot snapshot) {
+ final Map<ParcelUuid, Set<Integer>> subGrpMappings = new ArrayMap<>();
+ for (ParcelUuid subGrp : mVcns.keySet()) {
+ subGrpMappings.put(subGrp, snapshot.getAllSubIdsInGroup(subGrp));
+ }
+ return subGrpMappings;
+ }
+
+ @GuardedBy("mLock")
private void stopVcnLocked(@NonNull ParcelUuid uuidToTeardown) {
final Vcn vcnToTeardown = mVcns.remove(uuidToTeardown);
if (vcnToTeardown == null) {
@@ -516,7 +537,7 @@
@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.
@@ -538,7 +559,7 @@
@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);
@@ -564,7 +585,7 @@
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());
@@ -589,7 +610,7 @@
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);
@@ -815,6 +836,8 @@
if (isVcnManagedNetwork) {
ncBuilder.removeCapability(
NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED);
+ } else {
+ ncBuilder.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED);
}
if (isRestrictedCarrierWifi) {
@@ -823,8 +846,14 @@
}
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/am/LmkdConnection.java b/services/core/java/com/android/server/am/LmkdConnection.java
index f41c364..1ecb9eb 100644
--- a/services/core/java/com/android/server/am/LmkdConnection.java
+++ b/services/core/java/com/android/server/am/LmkdConnection.java
@@ -31,6 +31,8 @@
import libcore.io.IoUtils;
+import java.io.ByteArrayInputStream;
+import java.io.DataInputStream;
import java.io.FileDescriptor;
import java.io.IOException;
import java.io.InputStream;
@@ -43,8 +45,12 @@
public class LmkdConnection {
private static final String TAG = TAG_WITH_CLASS_NAME ? "LmkdConnection" : TAG_AM;
- // lmkd reply max size in bytes
- private static final int LMKD_REPLY_MAX_SIZE = 12;
+ /**
+ * Max LMKD reply packet length in bytes
+ * Used to hold the data for the statsd atoms logging
+ * Must be in sync with statslog.h
+ */
+ private static final int LMKD_REPLY_MAX_SIZE = 214;
// connection listener interface
interface LmkdConnectionListener {
@@ -70,7 +76,7 @@
* @param receivedLen Size of the data received
* @return True if the message has been handled correctly, false otherwise.
*/
- boolean handleUnsolicitedMessage(ByteBuffer dataReceived, int receivedLen);
+ boolean handleUnsolicitedMessage(DataInputStream inputData, int receivedLen);
}
private final MessageQueue mMsgQueue;
@@ -95,6 +101,10 @@
private final ByteBuffer mInputBuf =
ByteBuffer.allocate(LMKD_REPLY_MAX_SIZE);
+ // Input stream to parse the incoming data
+ private final DataInputStream mInputData = new DataInputStream(
+ new ByteArrayInputStream(mInputBuf.array()));
+
// object to protect mReplyBuf and to wait/notify when reply is received
private final Object mReplyBufLock = new Object();
@@ -186,26 +196,32 @@
private void processIncomingData() {
int len = read(mInputBuf);
if (len > 0) {
- synchronized (mReplyBufLock) {
- if (mReplyBuf != null) {
- if (mListener.isReplyExpected(mReplyBuf, mInputBuf, len)) {
- // copy into reply buffer
- mReplyBuf.put(mInputBuf.array(), 0, len);
- mReplyBuf.rewind();
- // wakeup the waiting thread
- mReplyBufLock.notifyAll();
- } else if (!mListener.handleUnsolicitedMessage(mInputBuf, len)) {
- // received unexpected packet
- // treat this as an error
- mReplyBuf = null;
- mReplyBufLock.notifyAll();
- Slog.e(TAG, "Received an unexpected packet from lmkd");
+ try {
+ // reset InputStream to point into mInputBuf.array() begin
+ mInputData.reset();
+ synchronized (mReplyBufLock) {
+ if (mReplyBuf != null) {
+ if (mListener.isReplyExpected(mReplyBuf, mInputBuf, len)) {
+ // copy into reply buffer
+ mReplyBuf.put(mInputBuf.array(), 0, len);
+ mReplyBuf.rewind();
+ // wakeup the waiting thread
+ mReplyBufLock.notifyAll();
+ } else if (!mListener.handleUnsolicitedMessage(mInputData, len)) {
+ // received unexpected packet
+ // treat this as an error
+ mReplyBuf = null;
+ mReplyBufLock.notifyAll();
+ Slog.e(TAG, "Received an unexpected packet from lmkd");
+ }
+ } else if (!mListener.handleUnsolicitedMessage(mInputData, len)) {
+ // received asynchronous communication from lmkd
+ // but we don't recognize it.
+ Slog.w(TAG, "Received an unexpected packet from lmkd");
}
- } else if (!mListener.handleUnsolicitedMessage(mInputBuf, len)) {
- // received asynchronous communication from lmkd
- // but we don't recognize it.
- Slog.w(TAG, "Received an unexpected packet from lmkd");
}
+ } catch (IOException e) {
+ Slog.e(TAG, "Failed to parse lmkd data buffer. Size = " + len);
}
}
}
diff --git a/services/core/java/com/android/server/am/LmkdStatsReporter.java b/services/core/java/com/android/server/am/LmkdStatsReporter.java
new file mode 100644
index 0000000..c702d78
--- /dev/null
+++ b/services/core/java/com/android/server/am/LmkdStatsReporter.java
@@ -0,0 +1,107 @@
+/*
+ * 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.am;
+
+import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
+import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
+
+import android.util.Slog;
+
+import com.android.internal.util.FrameworkStatsLog;
+
+import java.io.DataInputStream;
+import java.io.IOException;
+
+/**
+ * Activity manager communication with lmkd data handling and statsd atom logging
+ */
+public final class LmkdStatsReporter {
+
+ static final String TAG = TAG_WITH_CLASS_NAME ? "LmkdStatsReporter" : TAG_AM;
+
+ public static final int KILL_OCCURRED_MSG_SIZE = 80;
+ public static final int STATE_CHANGED_MSG_SIZE = 8;
+
+ private static final int PRESSURE_AFTER_KILL = 0;
+ private static final int NOT_RESPONDING = 1;
+ private static final int LOW_SWAP_AND_THRASHING = 2;
+ private static final int LOW_MEM_AND_SWAP = 3;
+ private static final int LOW_MEM_AND_THRASHING = 4;
+ private static final int DIRECT_RECL_AND_THRASHING = 5;
+ private static final int LOW_MEM_AND_SWAP_UTIL = 6;
+
+ /**
+ * Processes the LMK_KILL_OCCURRED packet data
+ * Logs the event when LMKD kills a process to reduce memory pressure.
+ * Code: LMK_KILL_OCCURRED = 51
+ */
+ public static void logKillOccurred(DataInputStream inputData) {
+ try {
+ final long pgFault = inputData.readLong();
+ final long pgMajFault = inputData.readLong();
+ final long rssInBytes = inputData.readLong();
+ final long cacheInBytes = inputData.readLong();
+ final long swapInBytes = inputData.readLong();
+ final long processStartTimeNS = inputData.readLong();
+ final int uid = inputData.readInt();
+ final int oomScore = inputData.readInt();
+ final int minOomScore = inputData.readInt();
+ final int freeMemKb = inputData.readInt();
+ final int freeSwapKb = inputData.readInt();
+ final int killReason = inputData.readInt();
+ final String procName = inputData.readUTF();
+
+ FrameworkStatsLog.write(FrameworkStatsLog.LMK_KILL_OCCURRED, uid, procName, oomScore,
+ pgFault, pgMajFault, rssInBytes, cacheInBytes, swapInBytes, processStartTimeNS,
+ minOomScore, freeMemKb, freeSwapKb, mapKillReason(killReason));
+ } catch (IOException e) {
+ Slog.e(TAG, "Invalid buffer data. Failed to log LMK_KILL_OCCURRED");
+ return;
+ }
+ }
+
+ /**
+ * Processes the LMK_STATE_CHANGED packet
+ * Logs the change in LMKD state which is used as start/stop boundaries for logging
+ * LMK_KILL_OCCURRED event.
+ * Code: LMK_STATE_CHANGED = 54
+ */
+ public static void logStateChanged(int state) {
+ FrameworkStatsLog.write(FrameworkStatsLog.LMK_STATE_CHANGED, state);
+ }
+
+ private static int mapKillReason(int reason) {
+ switch (reason) {
+ case PRESSURE_AFTER_KILL:
+ return FrameworkStatsLog.LMK_KILL_OCCURRED__REASON__PRESSURE_AFTER_KILL;
+ case NOT_RESPONDING:
+ return FrameworkStatsLog.LMK_KILL_OCCURRED__REASON__NOT_RESPONDING;
+ case LOW_SWAP_AND_THRASHING:
+ return FrameworkStatsLog.LMK_KILL_OCCURRED__REASON__LOW_SWAP_AND_THRASHING;
+ case LOW_MEM_AND_SWAP:
+ return FrameworkStatsLog.LMK_KILL_OCCURRED__REASON__LOW_MEM_AND_SWAP;
+ case LOW_MEM_AND_THRASHING:
+ return FrameworkStatsLog.LMK_KILL_OCCURRED__REASON__LOW_MEM_AND_THRASHING;
+ case DIRECT_RECL_AND_THRASHING:
+ return FrameworkStatsLog.LMK_KILL_OCCURRED__REASON__DIRECT_RECL_AND_THRASHING;
+ case LOW_MEM_AND_SWAP_UTIL:
+ return FrameworkStatsLog.LMK_KILL_OCCURRED__REASON__LOW_MEM_AND_SWAP_UTIL;
+ default:
+ return FrameworkStatsLog.LMK_KILL_OCCURRED__REASON__UNKNOWN;
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index 444418c..e9b0146 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -125,6 +125,7 @@
import dalvik.system.VMRuntime;
+import java.io.DataInputStream;
import java.io.File;
import java.io.FileDescriptor;
import java.io.IOException;
@@ -316,18 +317,25 @@
// LMK_GETKILLCNT
// LMK_SUBSCRIBE
// LMK_PROCKILL
+ // LMK_UPDATE_PROPS
+ // LMK_KILL_OCCURRED
+ // LMK_STATE_CHANGED
static final byte LMK_TARGET = 0;
static final byte LMK_PROCPRIO = 1;
static final byte LMK_PROCREMOVE = 2;
static final byte LMK_PROCPURGE = 3;
static final byte LMK_GETKILLCNT = 4;
static final byte LMK_SUBSCRIBE = 5;
- static final byte LMK_PROCKILL = 6; // Note: this is an unsolicated command
+ static final byte LMK_PROCKILL = 6; // Note: this is an unsolicited command
+ static final byte LMK_UPDATE_PROPS = 7;
+ static final byte LMK_KILL_OCCURRED = 8; // Msg to subscribed clients on kill occurred event
+ static final byte LMK_STATE_CHANGED = 9; // Msg to subscribed clients on state changed
// Low Memory Killer Daemon command codes.
// These must be kept in sync with async_event_type definitions in lmkd.h
//
static final int LMK_ASYNC_EVENT_KILL = 0;
+ static final int LMK_ASYNC_EVENT_STAT = 1;
// lmkd reconnect delay in msecs
private static final long LMKD_RECONNECT_DELAY_MS = 1000;
@@ -776,22 +784,44 @@
}
@Override
- public boolean handleUnsolicitedMessage(ByteBuffer dataReceived,
+ public boolean handleUnsolicitedMessage(DataInputStream inputData,
int receivedLen) {
if (receivedLen < 4) {
return false;
}
- switch (dataReceived.getInt(0)) {
- case LMK_PROCKILL:
- if (receivedLen != 12) {
+
+ try {
+ switch (inputData.readInt()) {
+ case LMK_PROCKILL:
+ if (receivedLen != 12) {
+ return false;
+ }
+ final int pid = inputData.readInt();
+ final int uid = inputData.readInt();
+ mAppExitInfoTracker.scheduleNoteLmkdProcKilled(pid, uid);
+ return true;
+ case LMK_KILL_OCCURRED:
+ if (receivedLen
+ < LmkdStatsReporter.KILL_OCCURRED_MSG_SIZE) {
+ return false;
+ }
+ LmkdStatsReporter.logKillOccurred(inputData);
+ return true;
+ case LMK_STATE_CHANGED:
+ if (receivedLen
+ != LmkdStatsReporter.STATE_CHANGED_MSG_SIZE) {
+ return false;
+ }
+ final int state = inputData.readInt();
+ LmkdStatsReporter.logStateChanged(state);
+ return true;
+ default:
return false;
- }
- mAppExitInfoTracker.scheduleNoteLmkdProcKilled(
- dataReceived.getInt(4), dataReceived.getInt(8));
- return true;
- default:
- return false;
+ }
+ } catch (IOException e) {
+ Slog.e(TAG, "Invalid buffer data. Failed to log LMK_KILL_OCCURRED");
}
+ return false;
}
}
);
@@ -1433,6 +1463,12 @@
buf.putInt(LMK_SUBSCRIBE);
buf.putInt(LMK_ASYNC_EVENT_KILL);
ostream.write(buf.array(), 0, buf.position());
+
+ // Subscribe for stats event notifications
+ buf = ByteBuffer.allocate(4 * 2);
+ buf.putInt(LMK_SUBSCRIBE);
+ buf.putInt(LMK_ASYNC_EVENT_STAT);
+ ostream.write(buf.array(), 0, buf.position());
} catch (IOException ex) {
return false;
}
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlShellCommand.java b/services/core/java/com/android/server/hdmi/HdmiControlShellCommand.java
index 740407c..e233084 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlShellCommand.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlShellCommand.java
@@ -86,6 +86,8 @@
pw.println(" Send a Vendor Command to the given target device");
pw.println(" setsystemaudiomode, setsam [on|off]");
pw.println(" Sets the System Audio Mode feature on or off on TV devices");
+ pw.println(" setarc [on|off]");
+ pw.println(" Sets the ARC feature on or off on TV devices");
}
private int handleShellCommand(String cmd) throws RemoteException {
@@ -100,6 +102,8 @@
case "setsystemaudiomode":
case "setsam":
return setSystemAudioMode(pw);
+ case "setarc":
+ return setArcMode(pw);
}
getErrPrintWriter().println("Unhandled command: " + cmd);
@@ -188,6 +192,27 @@
return mCecResult.get() == HdmiControlManager.RESULT_SUCCESS ? 0 : 1;
}
+ private int setArcMode(PrintWriter pw) throws RemoteException {
+ if (1 > getRemainingArgsCount()) {
+ throw new IllegalArgumentException(
+ "Please indicate if ARC mode should be turned \"on\" or \"off\".");
+ }
+
+ String arg = getNextArg();
+ if (arg.equals("on")) {
+ pw.println("Setting ARC mode on");
+ mBinderService.setArcMode(true);
+ } else if (arg.equals("off")) {
+ pw.println("Setting ARC mode off");
+ mBinderService.setArcMode(false);
+ } else {
+ throw new IllegalArgumentException(
+ "Please indicate if ARC mode should be turned \"on\" or \"off\".");
+ }
+
+ return 0;
+ }
+
private boolean receiveCallback(String command) {
try {
if (!mLatch.await(HdmiConfig.TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index c01a115..29c3dd9 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -6850,7 +6850,7 @@
.appendPath(record.getKey()).build())
.addFlags(Intent.FLAG_RECEIVER_FOREGROUND)
.putExtra(EXTRA_KEY, record.getKey()),
- PendingIntent.FLAG_UPDATE_CURRENT);
+ PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
mAlarmManager.setExactAndAllowWhileIdle(AlarmManager.ELAPSED_REALTIME_WAKEUP,
mSystemClock.elapsedRealtime() + record.getNotification().getTimeoutAfter(),
pi);
@@ -7713,6 +7713,21 @@
int rank, int count, boolean wasPosted, String listenerName) {
final String canceledKey = r.getKey();
+ // Get pending intent used to create alarm, use FLAG_NO_CREATE if PendingIntent
+ // does not already exist, then null will be returned.
+ final PendingIntent pi = PendingIntent.getBroadcast(getContext(),
+ REQUEST_CODE_TIMEOUT,
+ new Intent(ACTION_NOTIFICATION_TIMEOUT)
+ .setData(new Uri.Builder().scheme(SCHEME_TIMEOUT)
+ .appendPath(r.getKey()).build())
+ .addFlags(Intent.FLAG_RECEIVER_FOREGROUND),
+ PendingIntent.FLAG_NO_CREATE | PendingIntent.FLAG_IMMUTABLE);
+
+ // Cancel alarm corresponding to pi.
+ if (pi != null) {
+ mAlarmManager.cancel(pi);
+ }
+
// Record caller.
recordCallerLocked(r);
diff --git a/services/core/java/com/android/server/notification/SnoozeHelper.java b/services/core/java/com/android/server/notification/SnoozeHelper.java
index 9a9e733..da472be 100644
--- a/services/core/java/com/android/server/notification/SnoozeHelper.java
+++ b/services/core/java/com/android/server/notification/SnoozeHelper.java
@@ -37,6 +37,7 @@
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto;
import com.android.internal.util.XmlUtils;
+import com.android.server.pm.PackageManagerService;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
@@ -463,6 +464,7 @@
return PendingIntent.getBroadcast(mContext,
REQUEST_CODE_REPOST,
new Intent(REPOST_ACTION)
+ .setPackage(PackageManagerService.PLATFORM_PACKAGE_NAME)
.setData(new Uri.Builder().scheme(REPOST_SCHEME).appendPath(key).build())
.addFlags(Intent.FLAG_RECEIVER_FOREGROUND)
.putExtra(EXTRA_KEY, key)
diff --git a/services/core/java/com/android/server/pm/BackgroundDexOptService.java b/services/core/java/com/android/server/pm/BackgroundDexOptService.java
index acec93c..77c1c1d 100644
--- a/services/core/java/com/android/server/pm/BackgroundDexOptService.java
+++ b/services/core/java/com/android/server/pm/BackgroundDexOptService.java
@@ -16,7 +16,6 @@
package com.android.server.pm;
-import static com.android.server.pm.PackageManagerService.DEBUG_DEXOPT;
import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME;
import android.annotation.Nullable;
@@ -24,12 +23,13 @@
import android.app.job.JobParameters;
import android.app.job.JobScheduler;
import android.app.job.JobService;
+import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageInfo;
-import android.os.BatteryManager;
+import android.os.BatteryManagerInternal;
import android.os.Environment;
import android.os.ServiceManager;
import android.os.SystemProperties;
@@ -37,6 +37,7 @@
import android.os.storage.StorageManager;
import android.util.ArraySet;
import android.util.Log;
+import android.util.Slog;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.FrameworkStatsLog;
@@ -65,9 +66,7 @@
private static final int JOB_IDLE_OPTIMIZE = 800;
private static final int JOB_POST_BOOT_UPDATE = 801;
- private static final long IDLE_OPTIMIZATION_PERIOD = DEBUG
- ? TimeUnit.MINUTES.toMillis(1)
- : TimeUnit.DAYS.toMillis(1);
+ private static final long IDLE_OPTIMIZATION_PERIOD = TimeUnit.DAYS.toMillis(1);
private static ComponentName sDexoptServiceName = new ComponentName(
"android",
@@ -115,14 +114,24 @@
return;
}
- JobScheduler js = (JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE);
+ final JobScheduler js = context.getSystemService(JobScheduler.class);
// Schedule a one-off job which scans installed packages and updates
- // out-of-date oat files.
- js.schedule(new JobInfo.Builder(JOB_POST_BOOT_UPDATE, sDexoptServiceName)
- .setMinimumLatency(TimeUnit.MINUTES.toMillis(1))
- .setOverrideDeadline(TimeUnit.MINUTES.toMillis(1))
- .build());
+ // out-of-date oat files. Schedule it 10 minutes after the boot complete event,
+ // so that we don't overload the boot with additional dex2oat compilations.
+ context.registerReceiver(new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ js.schedule(new JobInfo.Builder(JOB_POST_BOOT_UPDATE, sDexoptServiceName)
+ .setMinimumLatency(TimeUnit.MINUTES.toMillis(10))
+ .setOverrideDeadline(TimeUnit.MINUTES.toMillis(60))
+ .build());
+ context.unregisterReceiver(this);
+ if (DEBUG) {
+ Slog.i(TAG, "BootBgDexopt scheduled");
+ }
+ }
+ }, new IntentFilter(Intent.ACTION_BOOT_COMPLETED));
// Schedule a daily job which scans installed packages and compiles
// those with fresh profiling data.
@@ -132,8 +141,8 @@
.setPeriodic(IDLE_OPTIMIZATION_PERIOD)
.build());
- if (DEBUG_DEXOPT) {
- Log.i(TAG, "Jobs scheduled");
+ if (DEBUG) {
+ Slog.d(TAG, "BgDexopt scheduled");
}
}
@@ -149,32 +158,11 @@
}
}
- // Returns the current battery level as a 0-100 integer.
- private int getBatteryLevel() {
- IntentFilter filter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
- Intent intent = registerReceiver(null, filter);
- int level = intent.getIntExtra(BatteryManager.EXTRA_LEVEL, -1);
- int scale = intent.getIntExtra(BatteryManager.EXTRA_SCALE, -1);
- boolean present = intent.getBooleanExtra(BatteryManager.EXTRA_PRESENT, true);
-
- if (!present) {
- // No battery, treat as if 100%, no possibility of draining battery.
- return 100;
- }
-
- if (level < 0 || scale <= 0) {
- // Battery data unavailable. This should never happen, so assume the worst.
- return 0;
- }
-
- return (100 * level / scale);
- }
-
private long getLowStorageThreshold(Context context) {
@SuppressWarnings("deprecation")
final long lowThreshold = StorageManager.from(context).getStorageLowBytes(mDataDir);
if (lowThreshold == 0) {
- Log.e(TAG, "Invalid low storage threshold");
+ Slog.e(TAG, "Invalid low storage threshold");
}
return lowThreshold;
@@ -198,9 +186,8 @@
private void postBootUpdate(JobParameters jobParams, PackageManagerService pm,
ArraySet<String> pkgs) {
- // Load low battery threshold from the system config. This is a 0-100 integer.
- final int lowBatteryThreshold = getResources().getInteger(
- com.android.internal.R.integer.config_lowBatteryWarningLevel);
+ final BatteryManagerInternal batteryManagerInternal =
+ LocalServices.getService(BatteryManagerInternal.class);
final long lowThreshold = getLowStorageThreshold(this);
mAbortPostBootUpdate.set(false);
@@ -215,20 +202,19 @@
// Different job, which supersedes this one, is running.
break;
}
- if (getBatteryLevel() < lowBatteryThreshold) {
+ if (batteryManagerInternal.getBatteryLevelLow()) {
// Rather bail than completely drain the battery.
break;
}
long usableSpace = mDataDir.getUsableSpace();
if (usableSpace < lowThreshold) {
// Rather bail than completely fill up the disk.
- Log.w(TAG, "Aborting background dex opt job due to low storage: " +
+ Slog.w(TAG, "Aborting background dex opt job due to low storage: " +
usableSpace);
break;
}
-
- if (DEBUG_DEXOPT) {
- Log.i(TAG, "Updating package " + pkg);
+ if (DEBUG) {
+ Slog.i(TAG, "Updating package " + pkg);
}
// Update package if needed. Note that there can be no race between concurrent
@@ -260,13 +246,13 @@
public void run() {
int result = idleOptimization(pm, pkgs, BackgroundDexOptService.this);
if (result == OPTIMIZE_PROCESSED) {
- Log.i(TAG, "Idle optimizations completed.");
+ Slog.i(TAG, "Idle optimizations completed.");
} else if (result == OPTIMIZE_ABORT_NO_SPACE_LEFT) {
- Log.w(TAG, "Idle optimizations aborted because of space constraints.");
+ Slog.w(TAG, "Idle optimizations aborted because of space constraints.");
} else if (result == OPTIMIZE_ABORT_BY_JOB_SCHEDULER) {
- Log.w(TAG, "Idle optimizations aborted by job scheduler.");
+ Slog.w(TAG, "Idle optimizations aborted by job scheduler.");
} else {
- Log.w(TAG, "Idle optimizations ended with unexpected code: " + result);
+ Slog.w(TAG, "Idle optimizations ended with unexpected code: " + result);
}
if (result != OPTIMIZE_ABORT_BY_JOB_SCHEDULER) {
// Abandon our timeslice and do not reschedule.
@@ -280,7 +266,7 @@
// Optimize the given packages and return the optimization result (one of the OPTIMIZE_* codes).
private int idleOptimization(PackageManagerService pm, ArraySet<String> pkgs,
Context context) {
- Log.i(TAG, "Performing idle optimizations");
+ Slog.i(TAG, "Performing idle optimizations");
// If post-boot update is still running, request that it exits early.
mExitPostBootUpdate.set(true);
mAbortIdleOptimization.set(false);
@@ -355,11 +341,15 @@
final long lowStorageThresholdForDowngrade = LOW_THRESHOLD_MULTIPLIER_FOR_DOWNGRADE
* lowStorageThreshold;
boolean shouldDowngrade = shouldDowngrade(lowStorageThresholdForDowngrade);
- Log.d(TAG, "Should Downgrade " + shouldDowngrade);
+ if (DEBUG) {
+ Slog.d(TAG, "Should Downgrade " + shouldDowngrade);
+ }
if (shouldDowngrade) {
Set<String> unusedPackages =
pm.getUnusedPackages(mDowngradeUnusedAppsThresholdInMillis);
- Log.d(TAG, "Unsused Packages " + String.join(",", unusedPackages));
+ if (DEBUG) {
+ Slog.d(TAG, "Unsused Packages " + String.join(",", unusedPackages));
+ }
if (!unusedPackages.isEmpty()) {
for (String pkg : unusedPackages) {
@@ -431,7 +421,9 @@
*/
private boolean downgradePackage(PackageManagerService pm, String pkg,
boolean isForPrimaryDex) {
- Log.d(TAG, "Downgrading " + pkg);
+ if (DEBUG) {
+ Slog.d(TAG, "Downgrading " + pkg);
+ }
boolean dex_opt_performed = false;
int reason = PackageManagerService.REASON_INACTIVE_PACKAGE_DOWNGRADE;
int dexoptFlags = DexoptOptions.DEXOPT_BOOT_COMPLETE
@@ -553,7 +545,7 @@
long usableSpace = mDataDir.getUsableSpace();
if (usableSpace < lowStorageThreshold) {
// Rather bail than completely fill up the disk.
- Log.w(TAG, "Aborting background dex opt job due to low storage: " + usableSpace);
+ Slog.w(TAG, "Aborting background dex opt job due to low storage: " + usableSpace);
return OPTIMIZE_ABORT_NO_SPACE_LEFT;
}
@@ -592,8 +584,8 @@
@Override
public boolean onStartJob(JobParameters params) {
- if (DEBUG_DEXOPT) {
- Log.i(TAG, "onStartJob");
+ if (DEBUG) {
+ Slog.i(TAG, "onStartJob");
}
// NOTE: PackageManagerService.isStorageLow uses a different set of criteria from
@@ -601,17 +593,13 @@
// restart with a period of ~1 minute.
PackageManagerService pm = (PackageManagerService)ServiceManager.getService("package");
if (pm.isStorageLow()) {
- if (DEBUG_DEXOPT) {
- Log.i(TAG, "Low storage, skipping this run");
- }
+ Slog.i(TAG, "Low storage, skipping this run");
return false;
}
final ArraySet<String> pkgs = pm.getOptimizablePackages();
if (pkgs.isEmpty()) {
- if (DEBUG_DEXOPT) {
- Log.i(TAG, "No packages to optimize");
- }
+ Slog.i(TAG, "No packages to optimize");
return false;
}
@@ -627,8 +615,8 @@
@Override
public boolean onStopJob(JobParameters params) {
- if (DEBUG_DEXOPT) {
- Log.i(TAG, "onStopJob");
+ if (DEBUG) {
+ Slog.d(TAG, "onStopJob");
}
if (params.getJobId() == JOB_POST_BOOT_UPDATE) {
@@ -649,7 +637,7 @@
private void notifyPinService(ArraySet<String> updatedPackages) {
PinnerService pinnerService = LocalServices.getService(PinnerService.class);
if (pinnerService != null) {
- Log.i(TAG, "Pinning optimized code " + updatedPackages);
+ Slog.i(TAG, "Pinning optimized code " + updatedPackages);
pinnerService.update(updatedPackages, false /* force */);
}
}
@@ -684,7 +672,7 @@
final String sysPropKey = "pm.dexopt.downgrade_after_inactive_days";
String sysPropValue = SystemProperties.get(sysPropKey);
if (sysPropValue == null || sysPropValue.isEmpty()) {
- Log.w(TAG, "SysProp " + sysPropKey + " not set");
+ Slog.w(TAG, "SysProp " + sysPropKey + " not set");
return Long.MAX_VALUE;
}
return TimeUnit.DAYS.toMillis(Long.parseLong(sysPropValue));
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 48fee0b..0d06426 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 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);
@@ -12528,6 +12530,7 @@
if (hasOldPkg) {
mPermissionManager.revokeRuntimePermissionsIfGroupChanged(pkg, oldPkg,
allPackageNames);
+ mPermissionManager.revokeStoragePermissionsIfScopeExpanded(pkg, oldPkg);
}
if (hasPermissionDefinitionChanges) {
mPermissionManager.revokeRuntimePermissionsIfPermissionDefinitionChanged(
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
index 3ffca02..b500e16 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -206,6 +206,9 @@
private static final int USER_PERMISSION_FLAGS = FLAG_PERMISSION_USER_SET
| FLAG_PERMISSION_USER_FIXED;
+ /** All storage permissions */
+ private static final List<String> STORAGE_PERMISSIONS = new ArrayList<>();
+
/** If the permission of the value is granted, so is the key */
private static final Map<String, String> FULLER_PERMISSION_MAP = new HashMap<>();
@@ -214,6 +217,9 @@
Manifest.permission.ACCESS_FINE_LOCATION);
FULLER_PERMISSION_MAP.put(Manifest.permission.INTERACT_ACROSS_USERS,
Manifest.permission.INTERACT_ACROSS_USERS_FULL);
+ STORAGE_PERMISSIONS.add(Manifest.permission.READ_EXTERNAL_STORAGE);
+ STORAGE_PERMISSIONS.add(Manifest.permission.WRITE_EXTERNAL_STORAGE);
+ STORAGE_PERMISSIONS.add(Manifest.permission.ACCESS_MEDIA_LOCATION);
}
/** Lock to protect internal data access */
@@ -2266,6 +2272,49 @@
}
/**
+ * If the app is updated, and has scoped storage permissions, then it is possible that the
+ * app updated in an attempt to get unscoped storage. If so, revoke all storage permissions.
+ * @param newPackage The new package that was installed
+ * @param oldPackage The old package that was updated
+ */
+ private void revokeStoragePermissionsIfScopeExpanded(
+ @NonNull AndroidPackage newPackage,
+ @NonNull AndroidPackage oldPackage,
+ @NonNull PermissionCallback permissionCallback) {
+ boolean downgradedSdk = oldPackage.getTargetSdkVersion() >= Build.VERSION_CODES.Q
+ && newPackage.getTargetSdkVersion() < Build.VERSION_CODES.Q;
+ boolean upgradedSdk = oldPackage.getTargetSdkVersion() < Build.VERSION_CODES.Q
+ && newPackage.getTargetSdkVersion() >= Build.VERSION_CODES.Q;
+ boolean newlyRequestsLegacy = !upgradedSdk && !oldPackage.isRequestLegacyExternalStorage()
+ && newPackage.isRequestLegacyExternalStorage();
+
+ if (!newlyRequestsLegacy && !downgradedSdk) {
+ return;
+ }
+
+ final int callingUid = Binder.getCallingUid();
+ final int userId = UserHandle.getUserId(newPackage.getUid());
+ int numRequestedPermissions = newPackage.getRequestedPermissions().size();
+ for (int i = 0; i < numRequestedPermissions; i++) {
+ PermissionInfo permInfo = getPermissionInfo(newPackage.getRequestedPermissions().get(i),
+ newPackage.getPackageName(), 0);
+ if (permInfo == null || !STORAGE_PERMISSIONS.contains(permInfo.name)) {
+ continue;
+ }
+
+ EventLog.writeEvent(0x534e4554, "171430330", newPackage.getUid(),
+ "Revoking permission " + permInfo.name + " from package "
+ + newPackage.getPackageName() + " as either the sdk downgraded "
+ + downgradedSdk + " or newly requested legacy full storage "
+ + newlyRequestsLegacy);
+
+ revokeRuntimePermissionInternal(permInfo.name, newPackage.getPackageName(),
+ false, callingUid, userId, null, permissionCallback);
+ }
+
+ }
+
+ /**
* We might auto-grant permissions if any permission of the group is already granted. Hence if
* the group of a granted permission changes we need to revoke it to avoid having permissions of
* the new group auto-granted.
@@ -4734,6 +4783,19 @@
@UserIdInt int userId) {
return PermissionManagerService.this.isPermissionsReviewRequired(pkg, userId);
}
+ /**
+ * If the app is updated, and has scoped storage permissions, then it is possible that the
+ * app updated in an attempt to get unscoped storage. If so, revoke all storage permissions.
+ * @param newPackage The new package that was installed
+ * @param oldPackage The old package that was updated
+ */
+ public void revokeStoragePermissionsIfScopeExpanded(
+ @NonNull AndroidPackage newPackage,
+ @NonNull AndroidPackage oldPackage
+ ) {
+ PermissionManagerService.this.revokeStoragePermissionsIfScopeExpanded(newPackage,
+ oldPackage, mDefaultPermissionCallback);
+ }
@Override
public void revokeRuntimePermissionsIfGroupChanged(
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java
index 393e852..a8e842f 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java
@@ -266,6 +266,17 @@
@NonNull ArrayList<String> allPackageNames);
/**
+ * If the app is updated, and has scoped storage permissions, then it is possible that the
+ * app updated in an attempt to get unscoped storage. If so, revoke all storage permissions.
+ * @param newPackage The new package that was installed
+ * @param oldPackage The old package that was updated
+ */
+ public abstract void revokeStoragePermissionsIfScopeExpanded(
+ @NonNull AndroidPackage newPackage,
+ @NonNull AndroidPackage oldPackage
+ );
+
+ /**
* Add all permissions in the given package.
* <p>
* NOTE: argument {@code groupTEMP} is temporary until mPermissionGroups is moved to
diff --git a/services/core/java/com/android/server/recoverysystem/RecoverySystemService.java b/services/core/java/com/android/server/recoverysystem/RecoverySystemService.java
index 24337f3..ed4a7bf 100644
--- a/services/core/java/com/android/server/recoverysystem/RecoverySystemService.java
+++ b/services/core/java/com/android/server/recoverysystem/RecoverySystemService.java
@@ -24,10 +24,14 @@
import static android.os.RecoverySystem.RESUME_ON_REBOOT_REBOOT_ERROR_UNSPECIFIED;
import static android.os.RecoverySystem.ResumeOnRebootRebootErrorCode;
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;
+import android.apex.CompressedApexInfoList;
import android.content.Context;
import android.content.IntentSender;
import android.content.SharedPreferences;
@@ -47,9 +51,11 @@
import android.os.ShellCallback;
import android.os.SystemProperties;
import android.provider.DeviceConfig;
+import android.sysprop.ApexProperties;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.FastImmutableArraySet;
+import android.util.Log;
import android.util.Slog;
import com.android.internal.annotations.GuardedBy;
@@ -59,6 +65,7 @@
import com.android.internal.widget.RebootEscrowListener;
import com.android.server.LocalServices;
import com.android.server.SystemService;
+import com.android.server.pm.ApexManager;
import libcore.io.IoUtils;
@@ -68,9 +75,13 @@
import java.io.FileDescriptor;
import java.io.FileWriter;
import java.io.IOException;
+import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.List;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
/**
* The recovery system service is responsible for coordinating recovery related
@@ -388,7 +399,13 @@
@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
@@ -554,12 +571,18 @@
case ROR_NEED_PREPARATION:
final long origId = Binder.clearCallingIdentity();
try {
- boolean result = mInjector.getLockSettingsService().prepareRebootEscrow();
- // Clear the RoR preparation state if lock settings reports an failure.
- if (!result) {
- clearRoRPreparationState();
+ LockSettingsInternal lockSettings = mInjector.getLockSettingsService();
+ if (lockSettings == null) {
+ Slog.e(TAG, "Failed to get lock settings service, skipping"
+ + " prepareRebootEscrow");
+ return false;
}
- return result;
+ // Clear the RoR preparation state if lock settings reports an failure.
+ if (!lockSettings.prepareRebootEscrow()) {
+ clearRoRPreparationState();
+ return false;
+ }
+ return true;
} finally {
Binder.restoreCallingIdentity(origId);
}
@@ -674,7 +697,14 @@
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);
}
@@ -768,7 +798,15 @@
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);
}
@@ -871,6 +909,76 @@
return rebootWithLskfImpl(packageName, reason, slotSwitch);
}
+ public static boolean isUpdatableApexSupported() {
+ return ApexProperties.updatable().orElse(false);
+ }
+
+ // Metadata should be no more than few MB, if it's larger than 100MB something is wrong.
+ private static final long APEX_INFO_SIZE_LIMIT = 24 * 1024 * 100;
+
+ private static CompressedApexInfoList getCompressedApexInfoList(String packageFile)
+ throws IOException {
+ try (ZipFile zipFile = new ZipFile(packageFile)) {
+ final ZipEntry entry = zipFile.getEntry("apex_info.pb");
+ if (entry == null) {
+ return null;
+ }
+ if (entry.getSize() >= APEX_INFO_SIZE_LIMIT) {
+ throw new IllegalArgumentException("apex_info.pb has size "
+ + entry.getSize()
+ + " which is larger than the permitted limit" + APEX_INFO_SIZE_LIMIT);
+ }
+ if (entry.getSize() == 0) {
+ CompressedApexInfoList infoList = new CompressedApexInfoList();
+ infoList.apexInfos = new CompressedApexInfo[0];
+ return infoList;
+ }
+ Log.i(TAG, "Allocating " + entry.getSize()
+ + " bytes of memory to store OTA Metadata");
+ byte[] data = new byte[(int) entry.getSize()];
+
+ try (InputStream is = zipFile.getInputStream(entry)) {
+ int bytesRead = is.read(data);
+ String msg = "Read " + bytesRead + " when expecting " + data.length;
+ Log.e(TAG, msg);
+ if (bytesRead != data.length) {
+ throw new IOException(msg);
+ }
+ }
+ ApexMetadata metadata = ApexMetadata.parseFrom(data);
+ CompressedApexInfoList apexInfoList = new CompressedApexInfoList();
+ apexInfoList.apexInfos =
+ Arrays.stream(metadata.apexInfo).filter(apex -> apex.isCompressed).map(apex -> {
+ CompressedApexInfo info = new CompressedApexInfo();
+ info.moduleName = apex.packageName;
+ info.decompressedSize = apex.decompressedSize;
+ info.versionCode = apex.version;
+ return info;
+ }).toArray(CompressedApexInfo[]::new);
+ return apexInfoList;
+ }
+ }
+
+ @Override
+ public boolean allocateSpaceForUpdate(String packageFile) {
+ if (!isUpdatableApexSupported()) {
+ Log.i(TAG, "Updatable Apex not supported, "
+ + "allocateSpaceForUpdate does nothing.");
+ return true;
+ }
+ try {
+ CompressedApexInfoList apexInfoList = getCompressedApexInfoList(packageFile);
+ ApexManager apexManager = ApexManager.getInstance();
+ apexManager.reserveSpaceForCompressedApex(apexInfoList);
+ return true;
+ } catch (RemoteException e) {
+ e.rethrowAsRuntimeException();
+ } catch (IOException | UnsupportedOperationException e) {
+ Slog.e(TAG, "Failed to reserve space for compressed apex: ", e);
+ }
+ return false;
+ }
+
@Override // Binder call
public boolean isLskfCaptured(String packageName) {
enforcePermissionForResumeOnReboot();
diff --git a/services/core/java/com/android/server/vcn/Vcn.java b/services/core/java/com/android/server/vcn/Vcn.java
index e1747f7..f918827 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.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 @@
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 @@
// 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 @@
}
private void handleTeardown() {
+ logDbg("Tearing down");
mVcnContext.getVcnNetworkProvider().unregisterListener(mRequestListener);
for (VcnGatewayConnection gatewayConnection : mVcnGatewayConnections.values()) {
@@ -350,6 +351,7 @@
}
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 @@
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 @@
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 @@
// 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 @@
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 @@
}
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 @@
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 @@
// 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 @@
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 @@
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 65b947c..5cecff6 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.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 @@
* <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 @@
@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 @@
// 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 @@
if (!mIsQuitting) {
mWakeLock.acquire();
+
+ logVdbg("Wakelock acquired: " + mWakeLock);
}
}
@@ -781,6 +791,8 @@
mVcnContext.ensureRunningOnLooperThread();
mWakeLock.release();
+
+ logVdbg("Wakelock released: " + mWakeLock);
}
/**
@@ -798,8 +810,7 @@
@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 @@
@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 @@
@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 @@
@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 @@
@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 @@
@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 @@
}
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 @@
}
private void cancelTeardownTimeoutAlarm() {
+ logVdbg("Cancelling teardown timeout alarm; mCurrentToken: " + mCurrentToken);
+
if (mTeardownTimeoutAlarm != null) {
mTeardownTimeoutAlarm.cancel();
mTeardownTimeoutAlarm = null;
@@ -960,6 +972,11 @@
}
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 @@
}
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 @@
}
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 @@
}
private void cancelRetryTimeoutAlarm() {
+ logVdbg("Cancel retry alarm; mCurrentToken: " + mCurrentToken);
+
if (mRetryTimeoutAlarm != null) {
mRetryTimeoutAlarm.cancel();
mRetryTimeoutAlarm = null;
@@ -1014,6 +1042,8 @@
@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 @@
}
private void cancelSafeModeAlarm() {
+ logVdbg("Cancel safe mode alarm; mCurrentToken: " + mCurrentToken);
+
if (mSafeModeTimeoutAlarm != null) {
mSafeModeTimeoutAlarm.cancel();
mSafeModeTimeoutAlarm = null;
@@ -1092,6 +1124,14 @@
+ exception.getMessage();
}
+ logDbg(
+ "Encountered error; code="
+ + errorCode
+ + ", exceptionClass="
+ + exceptionClass
+ + ", exceptionMessage="
+ + exceptionMessage);
+
mGatewayStatusCallback.onGatewayConnectionError(
mConnectionConfig.getGatewayConnectionName(),
errorCode,
@@ -1137,7 +1177,7 @@
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 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 @@
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 @@
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 @@
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 @@
}
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 @@
}
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 @@
@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 @@
@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 @@
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 @@
// 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 @@
setSafeModeAlarm();
break;
default:
- Slog.wtf(
- TAG,
+ logWtf(
"Unknown validation status "
+ status
+ "; ignoring");
@@ -1602,13 +1644,26 @@
@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 @@
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 @@
}
private void handleMigrationCompleted(EventMigrationCompletedInfo migrationCompletedInfo) {
+ logDbg("Migration completed: " + mUnderlying.network);
+
applyTransform(
mCurrentToken,
mTunnelIface,
@@ -1744,6 +1801,8 @@
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 @@
// 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 @@
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 @@
@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 @@
/** 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 @@
@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 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 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 0000000..93728ce
--- /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/services/core/java/com/android/server/wm/LockTaskController.java b/services/core/java/com/android/server/wm/LockTaskController.java
index c4a42ab..281d2c9 100644
--- a/services/core/java/com/android/server/wm/LockTaskController.java
+++ b/services/core/java/com/android/server/wm/LockTaskController.java
@@ -512,7 +512,7 @@
setStatusBarState(mLockTaskModeState, userId);
setKeyguardState(mLockTaskModeState, userId);
if (oldLockTaskModeState == LOCK_TASK_MODE_PINNED) {
- lockKeyguardIfNeeded();
+ lockKeyguardIfNeeded(userId);
}
if (getDevicePolicyManager() != null) {
getDevicePolicyManager().notifyLockTaskModeChanged(false, null, userId);
@@ -824,15 +824,15 @@
* Helper method for locking the device immediately. This may be necessary when the device
* leaves the pinned mode.
*/
- private void lockKeyguardIfNeeded() {
- if (shouldLockKeyguard()) {
+ private void lockKeyguardIfNeeded(int userId) {
+ if (shouldLockKeyguard(userId)) {
mWindowManager.lockNow(null);
mWindowManager.dismissKeyguard(null /* callback */, null /* message */);
getLockPatternUtils().requireCredentialEntry(USER_ALL);
}
}
- private boolean shouldLockKeyguard() {
+ private boolean shouldLockKeyguard(int userId) {
// This functionality should be kept consistent with
// com.android.settings.security.ScreenPinningSettings (see b/127605586)
try {
@@ -842,7 +842,7 @@
} catch (Settings.SettingNotFoundException e) {
// Log to SafetyNet for b/127605586
android.util.EventLog.writeEvent(0x534e4554, "127605586", -1, "");
- return getLockPatternUtils().isSecure(USER_CURRENT);
+ return getLockPatternUtils().isSecure(userId);
}
}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java
index 3deeea2..c2ead5f 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/SnoozeHelperTest.java
@@ -49,6 +49,7 @@
import com.android.internal.util.FastXmlSerializer;
import com.android.server.UiServiceTestCase;
+import com.android.server.pm.PackageManagerService;
import org.junit.Before;
import org.junit.Test;
@@ -257,6 +258,17 @@
}
@Test
+ public void testSnoozeSentToAndroid() throws Exception {
+ NotificationRecord r = getNotificationRecord("pkg", 1, "one", UserHandle.SYSTEM);
+ mSnoozeHelper.snooze(r, 1000);
+ ArgumentCaptor<PendingIntent> captor = ArgumentCaptor.forClass(PendingIntent.class);
+ verify(mAm, times(1)).setExactAndAllowWhileIdle(
+ anyInt(), anyLong(), captor.capture());
+ assertEquals(PackageManagerService.PLATFORM_PACKAGE_NAME,
+ captor.getValue().getIntent().getPackage());
+ }
+
+ @Test
public void testSnooze() throws Exception {
NotificationRecord r = getNotificationRecord("pkg", 1, "one", UserHandle.SYSTEM);
mSnoozeHelper.snooze(r, (String) null);
diff --git a/services/tests/wmtests/src/com/android/server/wm/LockTaskControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/LockTaskControllerTest.java
index e345bec..fdc01b4 100644
--- a/services/tests/wmtests/src/com/android/server/wm/LockTaskControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/LockTaskControllerTest.java
@@ -450,7 +450,7 @@
Settings.Secure.clearProviderForTest();
// AND a password is set
- when(mLockPatternUtils.isSecure(anyInt()))
+ when(mLockPatternUtils.isSecure(TEST_USER_ID))
.thenReturn(true);
// AND there is a task record
diff --git a/telecomm/java/android/telecom/CallerInfo.java b/telecomm/java/android/telecom/CallerInfo.java
index 2983e63..a63ee46 100644
--- a/telecomm/java/android/telecom/CallerInfo.java
+++ b/telecomm/java/android/telecom/CallerInfo.java
@@ -406,8 +406,7 @@
// Change the callerInfo number ONLY if it is an emergency number
// or if it is the voicemail number. If it is either, take a
// shortcut and skip the query.
- TelephonyManager tm = context.getSystemService(TelephonyManager.class);
- if (tm.isEmergencyNumber(number)) {
+ if (PhoneNumberUtils.isLocalEmergencyNumber(context, number)) {
return new CallerInfo().markAsEmergency(context);
} else if (PhoneNumberUtils.isVoiceMailNumber(null, subId, number)) {
return new CallerInfo().markAsVoiceMail(context, subId);
diff --git a/telecomm/java/android/telecom/CallerInfoAsyncQuery.java b/telecomm/java/android/telecom/CallerInfoAsyncQuery.java
index a9e1a8f..bf49f3c 100644
--- a/telecomm/java/android/telecom/CallerInfoAsyncQuery.java
+++ b/telecomm/java/android/telecom/CallerInfoAsyncQuery.java
@@ -483,7 +483,16 @@
// check to see if these are recognized numbers, and use shortcuts if we can.
TelephonyManager tm = context.getSystemService(TelephonyManager.class);
- if (tm.isEmergencyNumber(number)) {
+ boolean isEmergencyNumber = false;
+ try {
+ isEmergencyNumber = tm.isEmergencyNumber(number);
+ } catch (IllegalStateException ise) {
+ // Ignore the exception that Telephony is not up. Use PhoneNumberUtils API now.
+ // Ideally the PhoneNumberUtils API needs to be removed once the
+ // telphony service not up issue can be fixed (b/187412989)
+ isEmergencyNumber = PhoneNumberUtils.isLocalEmergencyNumber(context, number);
+ }
+ if (isEmergencyNumber) {
cw.event = EVENT_EMERGENCY_NUMBER;
} else if (PhoneNumberUtils.isVoiceMailNumber(context, subId, number)) {
cw.event = EVENT_VOICEMAIL_NUMBER;
diff --git a/telephony/common/android/telephony/LocationAccessPolicy.java b/telephony/common/android/telephony/LocationAccessPolicy.java
index 25b062f..502bfa3 100644
--- a/telephony/common/android/telephony/LocationAccessPolicy.java
+++ b/telephony/common/android/telephony/LocationAccessPolicy.java
@@ -86,8 +86,9 @@
private String mCallingFeatureId;
private int mCallingUid;
private int mCallingPid;
- private int mMinSdkVersionForCoarse = Integer.MAX_VALUE;
- private int mMinSdkVersionForFine = Integer.MAX_VALUE;
+ private int mMinSdkVersionForCoarse = -1;
+ private int mMinSdkVersionForFine = -1;
+ private int mMinSdkVersionForEnforcement = -1;
private boolean mLogAsInfo = false;
private String mMethod;
@@ -125,7 +126,14 @@
/**
* Apps that target at least this sdk version will be checked for coarse location
- * permission. Defaults to INT_MAX (which means don't check)
+ * permission. This method MUST be called before calling {@link #build()}. Otherwise, an
+ * {@link IllegalArgumentException} will be thrown.
+ *
+ * Additionally, if both the argument to this method and
+ * {@link #setMinSdkVersionForFine} are greater than {@link Build.VERSION_CODES#BASE},
+ * you must call {@link #setMinSdkVersionForEnforcement} with the min of the two to
+ * affirm that you do not want any location checks below a certain SDK version.
+ * Otherwise, {@link #build} will throw an {@link IllegalArgumentException}.
*/
public Builder setMinSdkVersionForCoarse(
int minSdkVersionForCoarse) {
@@ -135,7 +143,14 @@
/**
* Apps that target at least this sdk version will be checked for fine location
- * permission. Defaults to INT_MAX (which means don't check)
+ * permission. This method MUST be called before calling {@link #build()}.
+ * Otherwise, an {@link IllegalArgumentException} will be thrown.
+ *
+ * Additionally, if both the argument to this method and
+ * {@link #setMinSdkVersionForCoarse} are greater than {@link Build.VERSION_CODES#BASE},
+ * you must call {@link #setMinSdkVersionForEnforcement} with the min of the two to
+ * affirm that you do not want any location checks below a certain SDK version.
+ * Otherwise, {@link #build} will throw an {@link IllegalArgumentException}.
*/
public Builder setMinSdkVersionForFine(
int minSdkVersionForFine) {
@@ -144,6 +159,17 @@
}
/**
+ * If both the argument to {@link #setMinSdkVersionForFine} and
+ * {@link #setMinSdkVersionForCoarse} are greater than {@link Build.VERSION_CODES#BASE},
+ * this method must be called with the min of the two to
+ * affirm that you do not want any location checks below a certain SDK version.
+ */
+ public Builder setMinSdkVersionForEnforcement(int minSdkVersionForEnforcement) {
+ mMinSdkVersionForEnforcement = minSdkVersionForEnforcement;
+ return this;
+ }
+
+ /**
* Optional, for logging purposes only.
*/
public Builder setMethod(String method) {
@@ -161,6 +187,26 @@
/** build LocationPermissionQuery */
public LocationPermissionQuery build() {
+ if (mMinSdkVersionForCoarse < 0 || mMinSdkVersionForFine < 0) {
+ throw new IllegalArgumentException("Must specify min sdk versions for"
+ + " enforcement for both coarse and fine permissions");
+ }
+ if (mMinSdkVersionForFine > Build.VERSION_CODES.BASE
+ && mMinSdkVersionForCoarse > Build.VERSION_CODES.BASE) {
+ if (mMinSdkVersionForEnforcement != Math.min(
+ mMinSdkVersionForCoarse, mMinSdkVersionForFine)) {
+ throw new IllegalArgumentException("setMinSdkVersionForEnforcement must be"
+ + " called.");
+ }
+ }
+
+ if (mMinSdkVersionForFine < mMinSdkVersionForCoarse) {
+ throw new IllegalArgumentException("Since fine location permission includes"
+ + " access to coarse location, the min sdk level for enforcement of"
+ + " the fine location permission must not be less than the min sdk"
+ + " level for enforcement of the coarse location permission.");
+ }
+
return new LocationPermissionQuery(mCallingPackage, mCallingFeatureId,
mCallingUid, mCallingPid, mMinSdkVersionForCoarse, mMinSdkVersionForFine,
mLogAsInfo, mMethod);
diff --git a/telephony/common/com/android/internal/telephony/PackageChangeReceiver.java b/telephony/common/com/android/internal/telephony/PackageChangeReceiver.java
index 0b47547..e9b7d95 100644
--- a/telephony/common/com/android/internal/telephony/PackageChangeReceiver.java
+++ b/telephony/common/com/android/internal/telephony/PackageChangeReceiver.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package android.telephony;
+package com.android.internal.telephony;
import android.annotation.NonNull;
import android.annotation.Nullable;
diff --git a/telephony/common/com/android/internal/telephony/SmsApplication.java b/telephony/common/com/android/internal/telephony/SmsApplication.java
index b8b60da..16614c8 100644
--- a/telephony/common/com/android/internal/telephony/SmsApplication.java
+++ b/telephony/common/com/android/internal/telephony/SmsApplication.java
@@ -40,7 +40,6 @@
import android.os.UserHandle;
import android.provider.Telephony;
import android.provider.Telephony.Sms.Intents;
-import android.telephony.PackageChangeReceiver;
import android.telephony.TelephonyManager;
import android.util.Log;
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index dd474f0..f6ba7be 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -4106,6 +4106,26 @@
public static final String KEY_RCS_FEATURE_TAG_ALLOWED_STRING_ARRAY =
KEY_PREFIX + "rcs_feature_tag_allowed_string_array";
+ /**
+ * Flag indicating whether or not carrier forbids device send the RCS request when the
+ * device receive the network response with the SIP code 489 BAD EVENT.
+ * <p>
+ * The default value for this key is {@code false}.
+ * @hide
+ */
+ public static final String KEY_RCS_REQUEST_FORBIDDEN_BY_SIP_489_BOOL =
+ KEY_PREFIX + "rcs_request_forbidden_by_sip_489_bool";
+
+ /**
+ * Indicates the interval that SUBSCRIBE requests from applications will be retried at when
+ * the carrier network has responded to a previous request with a forbidden error.
+ * <p>
+ * The default value for this key is 20 minutes.
+ * @hide
+ */
+ public static final String KEY_RCS_REQUEST_RETRY_INTERVAL_MILLIS_LONG =
+ KEY_PREFIX + "rcs_request_retry_interval_millis_long";
+
private Ims() {}
private static PersistableBundle getDefaults() {
@@ -4119,6 +4139,8 @@
defaults.putBoolean(KEY_RCS_BULK_CAPABILITY_EXCHANGE_BOOL, false);
defaults.putBoolean(KEY_ENABLE_PRESENCE_GROUP_SUBSCRIBE_BOOL, true);
defaults.putInt(KEY_NON_RCS_CAPABILITIES_CACHE_EXPIRATION_SEC_INT, 30 * 24 * 60 * 60);
+ defaults.putBoolean(KEY_RCS_REQUEST_FORBIDDEN_BY_SIP_489_BOOL, false);
+ defaults.putLong(KEY_RCS_REQUEST_RETRY_INTERVAL_MILLIS_LONG, 20 * 60 * 1000);
defaults.putStringArray(KEY_RCS_FEATURE_TAG_ALLOWED_STRING_ARRAY, new String[]{
"+g.3gpp.icsi-ref=\"urn%3Aurn-7%3A3gpp-service.ims.icsi.oma.cpm.msg\"",
"+g.3gpp.icsi-ref=\"urn%3Aurn-7%3A3gpp-service.ims.icsi.oma.cpm.largemsg\"",
diff --git a/telephony/java/android/telephony/ims/ImsMmTelManager.java b/telephony/java/android/telephony/ims/ImsMmTelManager.java
index d1a893f..7403327 100644
--- a/telephony/java/android/telephony/ims/ImsMmTelManager.java
+++ b/telephony/java/android/telephony/ims/ImsMmTelManager.java
@@ -713,15 +713,8 @@
* @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 @@
*
* @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 c49121f..4713b76 100644
--- a/telephony/java/android/telephony/ims/RegistrationManager.java
+++ b/telephony/java/android/telephony/ims/RegistrationManager.java
@@ -82,6 +82,8 @@
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 23032f0..35324a3c 100644
--- a/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java
+++ b/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java
@@ -56,7 +56,8 @@
@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 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/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index 97078c3..3c55b7c 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -2398,6 +2398,12 @@
String getLastUcePidfXmlShell(int subId);
/**
+ * Remove UCE requests cannot be sent to the network status.
+ * Note: This is designed for a SHELL command only.
+ */
+ boolean removeUceRequestDisallowedStatus(int subId);
+
+ /**
* Set a SignalStrengthUpdateRequest to receive notification when Signal Strength breach the
* specified thresholds.
*/
diff --git a/test-base/Android.bp b/test-base/Android.bp
index 9bd639b..b58aa11 100644
--- a/test-base/Android.bp
+++ b/test-base/Android.bp
@@ -32,7 +32,7 @@
java_sdk_library {
name: "android.test.base",
- srcs: ["src/**/*.java"],
+ srcs: [":android-test-base-sources"],
errorprone: {
javacflags: ["-Xep:DepAnn:ERROR"],
@@ -66,7 +66,7 @@
name: "android.test.base_static",
installable: false,
- srcs: ["src/**/*.java"],
+ srcs: [":android-test-base-sources"],
errorprone: {
javacflags: ["-Xep:DepAnn:ERROR"],
@@ -114,6 +114,12 @@
],
}
+filegroup {
+ name: "android-test-base-sources",
+ srcs: ["src/**/*.java"],
+ path: "src",
+}
+
// Make the current.txt available for use by the cts/tests/signature tests.
// ========================================================================
filegroup {
diff --git a/test-mock/Android.bp b/test-mock/Android.bp
index b83bce6..107292c 100644
--- a/test-mock/Android.bp
+++ b/test-mock/Android.bp
@@ -29,7 +29,7 @@
name: "android.test.mock",
srcs: [
- "src/**/*.java",
+ ":android-test-mock-sources",
// Note: Below are NOT APIs of this library. We only take APIs under
// the android.test.mock package. They however provide private APIs that
// android.test.mock APIs references to.
@@ -61,3 +61,9 @@
"api/current.txt",
],
}
+
+filegroup {
+ name: "android-test-mock-sources",
+ srcs: ["src/**/*.java"],
+ path: "src",
+}
diff --git a/test-runner/Android.bp b/test-runner/Android.bp
index fe007e39..c380ae3 100644
--- a/test-runner/Android.bp
+++ b/test-runner/Android.bp
@@ -29,7 +29,7 @@
java_sdk_library {
name: "android.test.runner",
- srcs: ["src/**/*.java"],
+ srcs: [":android-test-runner-sources"],
errorprone: {
javacflags: ["-Xep:DepAnn:ERROR"],
@@ -76,7 +76,7 @@
java_library_static {
name: "repackaged.android.test.runner",
- srcs: ["src/**/*.java"],
+ srcs: [":android-test-runner-sources"],
exclude_srcs: [
"src/android/test/ActivityUnitTestCase.java",
"src/android/test/ApplicationTestCase.java",
@@ -108,3 +108,9 @@
"api/current.txt",
],
}
+
+filegroup {
+ name: "android-test-runner-sources",
+ srcs: ["src/**/*.java"],
+ path: "src",
+}
diff --git a/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java b/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java
index 32c95f1..cf2c9c7 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.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.Socket;
import java.util.Arrays;
import java.util.Collection;
+import java.util.Set;
/** Unit tests for {@link IpSecService}. */
@SmallTest
@@ -119,7 +126,18 @@
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 @@
@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 @@
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 @@
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 @@
}
@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 @@
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/VcnManagementServiceTest.java b/tests/vcn/java/com/android/server/VcnManagementServiceTest.java
index aa4b5f8..9ecd82f 100644
--- a/tests/vcn/java/com/android/server/VcnManagementServiceTest.java
+++ b/tests/vcn/java/com/android/server/VcnManagementServiceTest.java
@@ -77,6 +77,7 @@
import android.telephony.SubscriptionInfo;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
+import android.util.ArraySet;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
@@ -98,6 +99,7 @@
import java.util.Collections;
import java.util.List;
import java.util.Map;
+import java.util.Map.Entry;
import java.util.Set;
import java.util.UUID;
@@ -326,6 +328,17 @@
return subIdToGroupMap.get(invocation.getArgument(0));
}).when(snapshot).getGroupForSubId(anyInt());
+ doAnswer(invocation -> {
+ final ParcelUuid subGrp = invocation.getArgument(0);
+ final Set<Integer> subIds = new ArraySet<>();
+ for (Entry<Integer, ParcelUuid> entry : subIdToGroupMap.entrySet()) {
+ if (entry.getValue().equals(subGrp)) {
+ subIds.add(entry.getKey());
+ }
+ }
+ return subIds;
+ }).when(snapshot).getAllSubIdsInGroup(any());
+
final TelephonySubscriptionTrackerCallback cb = getTelephonySubscriptionTrackerCallback();
cb.onNewSnapshot(snapshot);
@@ -914,6 +927,18 @@
verify(mMockPolicyListener).onPolicyChanged();
}
+ @Test
+ public void testVcnSubIdChangeUpdatesPolicyListener() throws Exception {
+ startAndGetVcnInstance(TEST_UUID_2);
+ mVcnMgmtSvc.addVcnUnderlyingNetworkPolicyListener(mMockPolicyListener);
+
+ triggerSubscriptionTrackerCbAndGetSnapshot(
+ Collections.singleton(TEST_UUID_2),
+ Collections.singletonMap(TEST_SUBSCRIPTION_ID, TEST_UUID_2));
+
+ verify(mMockPolicyListener).onPolicyChanged();
+ }
+
private void triggerVcnSafeMode(
@NonNull ParcelUuid subGroup,
@NonNull TelephonySubscriptionSnapshot snapshot,
diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java
index eedaac4..39f7386 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.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 @@
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 @@
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 284f1f8..1ecb4c9 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 @@
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();
}