Merge "Remove framework code that has moved to frameworks/libs/net"
diff --git a/Android.bp b/Android.bp
index 79a8a4b..84f3fad 100644
--- a/Android.bp
+++ b/Android.bp
@@ -368,6 +368,7 @@
"devicepolicyprotosnano",
"com.android.sysprop.apex",
+ "com.android.sysprop.init",
"PlatformProperties",
],
sdk_version: "core_platform",
@@ -396,7 +397,6 @@
"ext",
"unsupportedappusage",
"updatable_media_stubs",
- "framework-tethering",
],
jarjar_rules: ":framework-jarjar-rules",
@@ -455,7 +455,9 @@
name: "framework-minus-apex",
defaults: ["framework-defaults"],
srcs: [":framework-non-updatable-sources"],
- libs: ["ike-stubs"],
+ libs: [
+ "framework-tethering-stubs",
+ ],
installable: true,
javac_shard_size: 150,
required: [
@@ -471,6 +473,10 @@
// For backwards compatibility.
stem: "framework",
apex_available: ["//apex_available:platform"],
+ visibility: [
+ // TODO(b/147128803) remove the below lines
+ "//frameworks/base/packages/Tethering/tests/unit",
+ ],
}
// This "framework" module is NOT installed to the device. It's
@@ -490,9 +496,7 @@
"framework-minus-apex",
"updatable_media_stubs",
"framework-sdkextensions-stubs-systemapi",
- // TODO(b/147200698): should be the stub of framework-tethering
- "framework-tethering",
- "ike-stubs",
+ "framework-tethering-stubs",
],
sdk_version: "core_platform",
apex_available: ["//apex_available:platform"],
@@ -608,7 +612,9 @@
"core/java/android/annotation/Nullable.java",
"core/java/android/annotation/IntDef.java",
"core/java/android/annotation/IntRange.java",
+ "core/java/android/annotation/RequiresPermission.java",
"core/java/android/annotation/SystemApi.java",
+ "core/java/android/annotation/TestApi.java",
"core/java/com/android/internal/annotations/GuardedBy.java",
"core/java/com/android/internal/annotations/VisibleForTesting.java",
],
@@ -671,17 +677,6 @@
],
}
-filegroup {
- name: "framework-tethering-annotations",
- srcs: [
- "core/java/android/annotation/NonNull.java",
- "core/java/android/annotation/Nullable.java",
- "core/java/android/annotation/RequiresPermission.java",
- "core/java/android/annotation/SystemApi.java",
- "core/java/android/annotation/TestApi.java",
- "core/java/com/android/internal/annotations/GuardedBy.java",
- ],
-}
// Build ext.jar
// ============================================================
java_library {
@@ -915,22 +910,6 @@
output_extension: "proto.h",
}
-
-subdirs = [
- "cmds/*",
- "core/*",
- "libs/*",
- "media/*",
- "proto",
- "tools/*",
- "native/android",
- "native/graphics/jni",
-]
-
-optional_subdirs = [
- "core/tests/utiltests/jni",
-]
-
// TODO(b/77285514): remove this once the last few hidl interfaces have been
// updated to use hwbinder.stubs.
java_library {
@@ -990,13 +969,6 @@
}
filegroup {
- name: "framework-annotation-nonnull-srcs",
- srcs: [
- "core/java/android/annotation/NonNull.java",
- ],
-}
-
-filegroup {
name: "framework-media-annotation-srcs",
srcs: [
"core/java/android/annotation/CallbackExecutor.java",
diff --git a/Android.mk b/Android.mk
index 815a169..09f2c40 100644
--- a/Android.mk
+++ b/Android.mk
@@ -75,12 +75,6 @@
# Run this for checkbuild
checkbuild: doc-comment-check-docs
-# ==== hiddenapi lists =======================================
-ifneq ($(UNSAFE_DISABLE_HIDDENAPI_FLAGS),true)
-$(call dist-for-goals,droidcore,$(INTERNAL_PLATFORM_HIDDENAPI_FLAGS))
-$(call dist-for-goals,droidcore,$(INTERNAL_PLATFORM_HIDDENAPI_GREYLIST_METADATA))
-endif # UNSAFE_DISABLE_HIDDENAPI_FLAGS
-
# Include subdirectory makefiles
# ============================================================
diff --git a/ApiDocs.bp b/ApiDocs.bp
index 04ddc50..b7e3646 100644
--- a/ApiDocs.bp
+++ b/ApiDocs.bp
@@ -356,10 +356,10 @@
dist: {
targets: ["docs"],
},
- cmd: "unzip $(location :ds-docs-java{.docs.zip}) -d $(genDir) && " +
- "unzip $(location :ds-docs-kt{.docs.zip}) -d $(genDir)/en/reference/kotlin && " +
+ cmd: "unzip -q $(location :ds-docs-java{.docs.zip}) -d $(genDir) && " +
+ "unzip -q $(location :ds-docs-kt{.docs.zip}) -d $(genDir)/en/reference/kotlin && " +
"SWITCHER=$$(cd $$(dirname $(location switcher4)) && pwd)/$$(basename $(location switcher4)) && " +
- "(cd $(genDir)/en/reference && $$SWITCHER --work platform) && " +
+ "(cd $(genDir)/en/reference && $$SWITCHER --work platform) > /dev/null && " +
"$(location soong_zip) -o $(out) -C $(genDir) -D $(genDir)",
}
diff --git a/StubLibraries.bp b/StubLibraries.bp
index 8abe64c..84b3625 100644
--- a/StubLibraries.bp
+++ b/StubLibraries.bp
@@ -49,7 +49,6 @@
":opt-net-voip-srcs",
":core-current-stubs-source",
":core_public_api_files",
- ":ike-api-srcs",
],
libs: ["framework-internal-utils"],
installable: false,
@@ -193,20 +192,17 @@
api_file: "api/module-lib-current.txt",
removed_api_file: "api/module-lib-removed.txt",
},
- // TODO(b/147559833) enable the compatibility check against the last release API
- // and the API lint
- //last_released: {
- // api_file: ":last-released-module-lib-api",
- // removed_api_file: "api/module-lib-removed.txt",
- // baseline_file: ":module-lib-api-incompatibilities-with-last-released"
- //},
- //api_lint: {
- // enabled: true,
- // new_since: ":last-released-module-lib-api",
- // baseline_file: "api/module-lib-lint-baseline.txt",
- //},
+ last_released: {
+ api_file: ":last-released-module-lib-api",
+ removed_api_file: "api/module-lib-removed.txt",
+ baseline_file: ":module-lib-api-incompatibilities-with-last-released"
+ },
+ api_lint: {
+ enabled: true,
+ new_since: ":last-released-module-lib-api",
+ baseline_file: "api/module-lib-lint-baseline.txt",
+ },
},
- //jdiff_enabled: true,
}
diff --git a/apex/Android.bp b/apex/Android.bp
index 362cf95..051986e 100644
--- a/apex/Android.bp
+++ b/apex/Android.bp
@@ -31,12 +31,12 @@
priv_apps = " " +
"--show-annotation android.annotation.SystemApi\\(" +
- "client=android.annotation.SystemApi.Client.PRIVILEGED_APPS," +
+ "client=android.annotation.SystemApi.Client.PRIVILEGED_APPS" +
"\\) "
module_libs = " " +
" --show-annotation android.annotation.SystemApi\\(" +
- "client=android.annotation.SystemApi.Client.MODULE_LIBRARIES," +
+ "client=android.annotation.SystemApi.Client.MODULE_LIBRARIES" +
"\\) "
stubs_defaults {
@@ -48,6 +48,7 @@
stubs_defaults {
name: "framework-module-stubs-defaults-systemapi",
args: mainline_stubs_args + priv_apps,
+ srcs: [":framework-annotations"],
installable: false,
}
@@ -59,11 +60,13 @@
stubs_defaults {
name: "framework-module-api-defaults-module_libs_api",
args: mainline_stubs_args + module_libs,
+ srcs: [":framework-annotations"],
installable: false,
}
stubs_defaults {
name: "framework-module-stubs-defaults-module_libs_api",
args: mainline_stubs_args + module_libs + priv_apps,
+ srcs: [":framework-annotations"],
installable: false,
}
diff --git a/apex/sdkextensions/Android.bp b/apex/sdkextensions/Android.bp
index 4c5c2b2..a22a948 100644
--- a/apex/sdkextensions/Android.bp
+++ b/apex/sdkextensions/Android.bp
@@ -22,13 +22,14 @@
binaries: [ "derive_sdk" ],
prebuilts: [ "cur_sdkinfo" ],
manifest: "manifest.json",
+ min_sdk_version: "current",
}
apex_defaults {
name: "com.android.sdkext-defaults",
+ updatable: true,
java_libs: [ "framework-sdkextensions" ],
prebuilts: [
- "com.android.sdkext.ldconfig",
"derive_sdk.rc",
],
key: "com.android.sdkext.key",
@@ -51,13 +52,6 @@
certificate: "com.android.sdkext",
}
-prebuilt_etc {
- name: "com.android.sdkext.ldconfig",
- src: "ld.config.txt",
- filename: "ld.config.txt",
- installable: false,
-}
-
python_binary_host {
name: "gen_sdkinfo",
srcs: [
diff --git a/apex/sdkextensions/ld.config.txt b/apex/sdkextensions/ld.config.txt
deleted file mode 100644
index dcc69b8..0000000
--- a/apex/sdkextensions/ld.config.txt
+++ /dev/null
@@ -1,31 +0,0 @@
-# Copyright (C) 2019 The Android Open Source Project
-#
-# Bionic loader config file for the sdkextensions apex.
-
-dir.sdkextensions = /apex/com.android.sdkext/bin/
-
-[sdkextensions]
-additional.namespaces = platform
-
-namespace.default.isolated = true
-namespace.default.links = platform
-namespace.default.link.platform.allow_all_shared_libs = true
-
-###############################################################################
-# "platform" namespace: used for NDK libraries
-###############################################################################
-namespace.platform.isolated = true
-namespace.platform.search.paths = /system/${LIB}
-namespace.platform.asan.search.paths = /data/asan/system/${LIB}
-
-# /system/lib/libc.so, etc are symlinks to /apex/com.android.lib/lib/bionic/libc.so, etc.
-# Add /apex/... path to the permitted paths because linker uses realpath(3)
-# to check the accessibility of the lib. We could add this to search.paths
-# instead but that makes the resolution of bionic libs be dependent on
-# the order of /system/lib and /apex/... in search.paths. If /apex/...
-# is after /system/lib, then /apex/... is never tried because libc.so
-# is always found in /system/lib but fails to pass the accessibility test
-# because of its realpath. It's better to not depend on the ordering if
-# possible.
-namespace.platform.permitted.paths = /apex/com.android.runtime/${LIB}/bionic
-namespace.platform.asan.permitted.paths = /apex/com.android.runtime/${LIB}/bionic
diff --git a/api/current.txt b/api/current.txt
index 80a7bb4..e7877aa 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -11775,6 +11775,7 @@
field public static final String FEATURE_VR_HEADTRACKING = "android.hardware.vr.headtracking";
field @Deprecated public static final String FEATURE_VR_MODE = "android.software.vr.mode";
field public static final String FEATURE_VR_MODE_HIGH_PERFORMANCE = "android.hardware.vr.high_performance";
+ field public static final String FEATURE_VULKAN_DEQP_LEVEL = "android.software.vulkan.deqp.level";
field public static final String FEATURE_VULKAN_HARDWARE_COMPUTE = "android.hardware.vulkan.compute";
field public static final String FEATURE_VULKAN_HARDWARE_LEVEL = "android.hardware.vulkan.level";
field public static final String FEATURE_VULKAN_HARDWARE_VERSION = "android.hardware.vulkan.version";
@@ -28356,6 +28357,7 @@
method public int getAudioChannelCount();
method public int getAudioSampleRate();
method public CharSequence getDescription();
+ method @Nullable public String getEncoding();
method public android.os.Bundle getExtra();
method public String getId();
method public String getLanguage();
@@ -28383,6 +28385,7 @@
method @NonNull public android.media.tv.TvTrackInfo.Builder setAudioDescription(boolean);
method public android.media.tv.TvTrackInfo.Builder setAudioSampleRate(int);
method public android.media.tv.TvTrackInfo.Builder setDescription(CharSequence);
+ method @NonNull public android.media.tv.TvTrackInfo.Builder setEncoding(@Nullable String);
method @NonNull public android.media.tv.TvTrackInfo.Builder setEncrypted(boolean);
method public android.media.tv.TvTrackInfo.Builder setExtra(android.os.Bundle);
method @NonNull public android.media.tv.TvTrackInfo.Builder setHardOfHearing(boolean);
@@ -29749,7 +29752,7 @@
public class AudioGroup {
ctor @Deprecated public AudioGroup();
- ctor public AudioGroup(@Nullable android.content.Context);
+ ctor public AudioGroup(@NonNull android.content.Context);
method public void clear();
method public int getMode();
method public android.net.rtp.AudioStream[] getStreams();
@@ -43833,7 +43836,7 @@
method public final android.telecom.CallAudioState getCallAudioState();
method public final String getCallerDisplayName();
method public final int getCallerDisplayNamePresentation();
- method public int getCallerNumberVerificationStatus();
+ method public final int getCallerNumberVerificationStatus();
method public final android.telecom.Conference getConference();
method public final java.util.List<android.telecom.Conferenceable> getConferenceables();
method public final int getConnectionCapabilities();
@@ -43886,7 +43889,7 @@
method public final void setAudioModeIsVoip(boolean);
method public final void setAudioRoute(int);
method public final void setCallerDisplayName(String, int);
- method public void setCallerNumberVerificationStatus(int);
+ method public final void setCallerNumberVerificationStatus(int);
method public final void setConferenceableConnections(java.util.List<android.telecom.Connection>);
method public final void setConferenceables(java.util.List<android.telecom.Conferenceable>);
method public final void setConnectionCapabilities(int);
@@ -44701,8 +44704,8 @@
field public static final String KEY_ALLOW_ADD_CALL_DURING_VIDEO_CALL_BOOL = "allow_add_call_during_video_call";
field public static final String KEY_ALLOW_EMERGENCY_NUMBERS_IN_CALL_LOG_BOOL = "allow_emergency_numbers_in_call_log_bool";
field public static final String KEY_ALLOW_EMERGENCY_VIDEO_CALLS_BOOL = "allow_emergency_video_calls_bool";
- field public static final String KEY_ALLOW_HOLDING_VIDEO_CALL_BOOL = "allow_holding_video_call";
field public static final String KEY_ALLOW_HOLD_CALL_DURING_EMERGENCY_BOOL = "allow_hold_call_during_emergency_bool";
+ field public static final String KEY_ALLOW_HOLD_VIDEO_CALL_BOOL = "allow_hold_video_call_bool";
field public static final String KEY_ALLOW_LOCAL_DTMF_TONES_BOOL = "allow_local_dtmf_tones_bool";
field public static final String KEY_ALLOW_MERGE_WIFI_CALLS_WHEN_VOWIFI_OFF_BOOL = "allow_merge_wifi_calls_when_vowifi_off_bool";
field public static final String KEY_ALLOW_NON_EMERGENCY_CALLS_IN_ECM_BOOL = "allow_non_emergency_calls_in_ecm_bool";
@@ -45348,32 +45351,8 @@
public final class PhoneCapability implements android.os.Parcelable {
method public int describeContents();
- method @NonNull public java.util.List<java.lang.Integer> getBands(int);
- method @NonNull public java.util.List<java.util.List<java.lang.Long>> getConcurrentFeaturesSupport();
- method @NonNull public java.util.List<java.lang.String> getLogicalModemUuids();
- method public int getMaxActiveDedicatedBearers();
- method public int getMaxActiveInternetData();
- method public int getMaxActivePsVoice();
- method public long getPsDataConnectionLingerTimeMillis();
- method @NonNull public java.util.List<android.telephony.SimSlotCapability> getSimSlotCapabilities();
- method public long getSupportedRats();
- method public int getUeCategory(boolean, int);
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.telephony.PhoneCapability> CREATOR;
- field public static final long MODEM_FEATURE_3GPP2_REG = 1L; // 0x1L
- field public static final long MODEM_FEATURE_3GPP_REG = 2L; // 0x2L
- field public static final long MODEM_FEATURE_CDMA2000_EHRPD_REG = 4L; // 0x4L
- field public static final long MODEM_FEATURE_CSIM = 8192L; // 0x2000L
- field public static final long MODEM_FEATURE_CS_VOICE_SESSION = 512L; // 0x200L
- field public static final long MODEM_FEATURE_DEDICATED_BEARER = 2048L; // 0x800L
- field public static final long MODEM_FEATURE_EUTRAN_REG = 32L; // 0x20L
- field public static final long MODEM_FEATURE_EUTRA_NR_DUAL_CONNECTIVITY_REG = 128L; // 0x80L
- field public static final long MODEM_FEATURE_GERAN_REG = 8L; // 0x8L
- field public static final long MODEM_FEATURE_INTERACTIVE_DATA_SESSION = 1024L; // 0x400L
- field public static final long MODEM_FEATURE_NETWORK_SCAN = 4096L; // 0x1000L
- field public static final long MODEM_FEATURE_NGRAN_REG = 64L; // 0x40L
- field public static final long MODEM_FEATURE_PS_VOICE_REG = 256L; // 0x100L
- field public static final long MODEM_FEATURE_UTRAN_REG = 16L; // 0x10L
}
public class PhoneNumberFormattingTextWatcher implements android.text.TextWatcher {
@@ -45558,18 +45537,6 @@
field public static final int INVALID = 2147483647; // 0x7fffffff
}
- public final class SimSlotCapability implements android.os.Parcelable {
- method public int describeContents();
- method public int getPhysicalSlotIndex();
- method public int getSlotType();
- method public void writeToParcel(@NonNull android.os.Parcel, int);
- field @NonNull public static final android.os.Parcelable.Creator<android.telephony.SimSlotCapability> CREATOR;
- field public static final int SLOT_TYPE_EUICC = 3; // 0x3
- field public static final int SLOT_TYPE_IUICC = 2; // 0x2
- field public static final int SLOT_TYPE_SOFT_SIM = 4; // 0x4
- field public static final int SLOT_TYPE_UICC = 1; // 0x1
- }
-
public final class SmsManager {
method public String createAppSpecificSmsToken(android.app.PendingIntent);
method @Nullable public String createAppSpecificSmsTokenWithPackageInfo(@Nullable String, @NonNull android.app.PendingIntent);
@@ -45916,7 +45883,6 @@
method @RequiresPermission(anyOf={"android.permission.READ_PRIVILEGED_PHONE_STATE", android.Manifest.permission.READ_PRECISE_PHONE_STATE}) public int getNetworkSelectionMode();
method public String getNetworkSpecifier();
method @Deprecated @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public int getNetworkType();
- method @Nullable @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public android.telephony.PhoneCapability getPhoneCapability();
method @Deprecated public int getPhoneCount();
method public int getPhoneType();
method @RequiresPermission(anyOf={"android.permission.READ_PRIVILEGED_PHONE_STATE", android.Manifest.permission.READ_PHONE_STATE}) public int getPreferredOpportunisticDataSubscription();
@@ -69783,6 +69749,7 @@
method public static java.time.chrono.JapaneseEra[] values();
field public static final java.time.chrono.JapaneseEra HEISEI;
field public static final java.time.chrono.JapaneseEra MEIJI;
+ field public static final java.time.chrono.JapaneseEra REIWA;
field public static final java.time.chrono.JapaneseEra SHOWA;
field public static final java.time.chrono.JapaneseEra TAISHO;
}
diff --git a/api/lint-baseline.txt b/api/lint-baseline.txt
index 5c31f41..b14a12f 100644
--- a/api/lint-baseline.txt
+++ b/api/lint-baseline.txt
@@ -537,6 +537,9 @@
MissingNullability: android.icu.util.VersionInfo#UNICODE_13_0:
Missing nullability on field `UNICODE_13_0` in class `class android.icu.util.VersionInfo`
+
+MissingNullability: java.time.chrono.JapaneseEra#REIWA:
+ Missing nullability on field `REIWA` in class `class java.time.chrono.JapaneseEra`
RequiresPermission: android.accounts.AccountManager#getAccountsByTypeAndFeatures(String, String[], android.accounts.AccountManagerCallback<android.accounts.Account[]>, android.os.Handler):
diff --git a/api/module-lib-current.txt b/api/module-lib-current.txt
index 2831924..1b72fc3 100644
--- a/api/module-lib-current.txt
+++ b/api/module-lib-current.txt
@@ -9,3 +9,123 @@
}
+package android.net {
+
+ public final class TetheredClient implements android.os.Parcelable {
+ ctor public TetheredClient(@NonNull android.net.MacAddress, @NonNull java.util.Collection<android.net.TetheredClient.AddressInfo>, int);
+ method public int describeContents();
+ method @NonNull public java.util.List<android.net.TetheredClient.AddressInfo> getAddresses();
+ method @NonNull public android.net.MacAddress getMacAddress();
+ method public int getTetheringType();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.net.TetheredClient> CREATOR;
+ }
+
+ public static final class TetheredClient.AddressInfo implements android.os.Parcelable {
+ method public int describeContents();
+ method @NonNull public android.net.LinkAddress getAddress();
+ method @Nullable public String getHostname();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.net.TetheredClient.AddressInfo> CREATOR;
+ }
+
+ public class TetheringConstants {
+ field public static final String EXTRA_ADD_TETHER_TYPE = "extraAddTetherType";
+ field public static final String EXTRA_PROVISION_CALLBACK = "extraProvisionCallback";
+ field public static final String EXTRA_REM_TETHER_TYPE = "extraRemTetherType";
+ field public static final String EXTRA_RUN_PROVISION = "extraRunProvision";
+ field public static final String EXTRA_SET_ALARM = "extraSetAlarm";
+ }
+
+ public class TetheringManager {
+ ctor public TetheringManager(@NonNull android.content.Context, @NonNull java.util.function.Supplier<android.os.IBinder>);
+ method public int getLastTetherError(@NonNull String);
+ method @NonNull public String[] getTetherableBluetoothRegexs();
+ method @NonNull public String[] getTetherableIfaces();
+ method @NonNull public String[] getTetherableUsbRegexs();
+ method @NonNull public String[] getTetherableWifiRegexs();
+ method @NonNull public String[] getTetheredIfaces();
+ method @NonNull public String[] getTetheringErroredIfaces();
+ method public boolean isTetheringSupported();
+ method @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE) public void registerTetheringEventCallback(@NonNull java.util.concurrent.Executor, @NonNull android.net.TetheringManager.TetheringEventCallback);
+ method @RequiresPermission(anyOf={"android.permission.TETHER_PRIVILEGED", android.Manifest.permission.WRITE_SETTINGS}) public void requestLatestTetheringEntitlementResult(int, boolean, @NonNull java.util.concurrent.Executor, @NonNull android.net.TetheringManager.OnTetheringEntitlementResultListener);
+ method public void requestLatestTetheringEntitlementResult(int, @NonNull android.os.ResultReceiver, boolean);
+ method @Deprecated public int setUsbTethering(boolean);
+ method @RequiresPermission(anyOf={"android.permission.TETHER_PRIVILEGED", android.Manifest.permission.WRITE_SETTINGS}) public void startTethering(@NonNull android.net.TetheringManager.TetheringRequest, @NonNull java.util.concurrent.Executor, @NonNull android.net.TetheringManager.StartTetheringCallback);
+ method @RequiresPermission(anyOf={"android.permission.TETHER_PRIVILEGED", android.Manifest.permission.WRITE_SETTINGS}) public void startTethering(int, @NonNull java.util.concurrent.Executor, @NonNull android.net.TetheringManager.StartTetheringCallback);
+ method @RequiresPermission(anyOf={"android.permission.TETHER_PRIVILEGED", android.Manifest.permission.WRITE_SETTINGS}) public void stopAllTethering();
+ method @RequiresPermission(anyOf={"android.permission.TETHER_PRIVILEGED", android.Manifest.permission.WRITE_SETTINGS}) public void stopTethering(int);
+ method @Deprecated public int tether(@NonNull String);
+ method @RequiresPermission(anyOf={"android.permission.TETHER_PRIVILEGED", android.Manifest.permission.ACCESS_NETWORK_STATE}) public void unregisterTetheringEventCallback(@NonNull android.net.TetheringManager.TetheringEventCallback);
+ method @Deprecated public int untether(@NonNull String);
+ field public static final String ACTION_TETHER_STATE_CHANGED = "android.net.conn.TETHER_STATE_CHANGED";
+ field public static final String EXTRA_ACTIVE_LOCAL_ONLY = "android.net.extra.ACTIVE_LOCAL_ONLY";
+ field public static final String EXTRA_ACTIVE_TETHER = "tetherArray";
+ field public static final String EXTRA_AVAILABLE_TETHER = "availableArray";
+ field public static final String EXTRA_ERRORED_TETHER = "erroredArray";
+ field public static final int TETHERING_BLUETOOTH = 2; // 0x2
+ field public static final int TETHERING_ETHERNET = 5; // 0x5
+ field public static final int TETHERING_INVALID = -1; // 0xffffffff
+ field public static final int TETHERING_NCM = 4; // 0x4
+ field public static final int TETHERING_USB = 1; // 0x1
+ field public static final int TETHERING_WIFI = 0; // 0x0
+ field public static final int TETHERING_WIFI_P2P = 3; // 0x3
+ field public static final int TETHER_ERROR_DHCPSERVER_ERROR = 12; // 0xc
+ field public static final int TETHER_ERROR_DISABLE_NAT_ERROR = 9; // 0x9
+ field public static final int TETHER_ERROR_ENABLE_NAT_ERROR = 8; // 0x8
+ field public static final int TETHER_ERROR_ENTITLEMENT_UNKNOWN = 13; // 0xd
+ field public static final int TETHER_ERROR_IFACE_CFG_ERROR = 10; // 0xa
+ field public static final int TETHER_ERROR_MASTER_ERROR = 5; // 0x5
+ field public static final int TETHER_ERROR_NO_ACCESS_TETHERING_PERMISSION = 15; // 0xf
+ field public static final int TETHER_ERROR_NO_CHANGE_TETHERING_PERMISSION = 14; // 0xe
+ field public static final int TETHER_ERROR_NO_ERROR = 0; // 0x0
+ field public static final int TETHER_ERROR_PROVISION_FAILED = 11; // 0xb
+ field public static final int TETHER_ERROR_SERVICE_UNAVAIL = 2; // 0x2
+ field public static final int TETHER_ERROR_TETHER_IFACE_ERROR = 6; // 0x6
+ field public static final int TETHER_ERROR_UNAVAIL_IFACE = 4; // 0x4
+ field public static final int TETHER_ERROR_UNKNOWN_IFACE = 1; // 0x1
+ field public static final int TETHER_ERROR_UNSUPPORTED = 3; // 0x3
+ field public static final int TETHER_ERROR_UNTETHER_IFACE_ERROR = 7; // 0x7
+ }
+
+ public static interface TetheringManager.OnTetheringEntitlementResultListener {
+ method public void onTetheringEntitlementResult(int);
+ }
+
+ public abstract static class TetheringManager.StartTetheringCallback {
+ ctor public TetheringManager.StartTetheringCallback();
+ method public void onTetheringFailed(int);
+ method public void onTetheringStarted();
+ }
+
+ public abstract static class TetheringManager.TetheringEventCallback {
+ ctor public TetheringManager.TetheringEventCallback();
+ method public void onClientsChanged(@NonNull java.util.Collection<android.net.TetheredClient>);
+ method public void onError(@NonNull String, int);
+ method @Deprecated public void onTetherableInterfaceRegexpsChanged(@NonNull android.net.TetheringManager.TetheringInterfaceRegexps);
+ method public void onTetherableInterfacesChanged(@NonNull java.util.List<java.lang.String>);
+ method public void onTetheredInterfacesChanged(@NonNull java.util.List<java.lang.String>);
+ method public void onTetheringSupported(boolean);
+ method public void onUpstreamChanged(@Nullable android.net.Network);
+ }
+
+ @Deprecated public static class TetheringManager.TetheringInterfaceRegexps {
+ ctor @Deprecated public TetheringManager.TetheringInterfaceRegexps(@NonNull String[], @NonNull String[], @NonNull String[]);
+ method @Deprecated @NonNull public java.util.List<java.lang.String> getTetherableBluetoothRegexs();
+ method @Deprecated @NonNull public java.util.List<java.lang.String> getTetherableUsbRegexs();
+ method @Deprecated @NonNull public java.util.List<java.lang.String> getTetherableWifiRegexs();
+ }
+
+ public static class TetheringManager.TetheringRequest {
+ }
+
+ public static class TetheringManager.TetheringRequest.Builder {
+ ctor public TetheringManager.TetheringRequest.Builder(int);
+ method @NonNull public android.net.TetheringManager.TetheringRequest build();
+ method @NonNull @RequiresPermission("android.permission.TETHER_PRIVILEGED") public android.net.TetheringManager.TetheringRequest.Builder setExemptFromEntitlementCheck(boolean);
+ method @NonNull @RequiresPermission("android.permission.TETHER_PRIVILEGED") public android.net.TetheringManager.TetheringRequest.Builder setSilentProvisioning(boolean);
+ method @NonNull @RequiresPermission("android.permission.TETHER_PRIVILEGED") public android.net.TetheringManager.TetheringRequest.Builder useStaticIpv4Addresses(@NonNull android.net.LinkAddress);
+ }
+
+}
+
diff --git a/api/module-lib-lint-baseline.txt b/api/module-lib-lint-baseline.txt
new file mode 100644
index 0000000..56f7a02
--- /dev/null
+++ b/api/module-lib-lint-baseline.txt
@@ -0,0 +1,29 @@
+// Baseline format: 1.0
+ActionValue: android.net.TetheringConstants#EXTRA_ADD_TETHER_TYPE:
+ Inconsistent extra value; expected `android.net.extra.ADD_TETHER_TYPE`, was `extraAddTetherType`
+ActionValue: android.net.TetheringConstants#EXTRA_PROVISION_CALLBACK:
+ Inconsistent extra value; expected `android.net.extra.PROVISION_CALLBACK`, was `extraProvisionCallback`
+ActionValue: android.net.TetheringConstants#EXTRA_REM_TETHER_TYPE:
+ Inconsistent extra value; expected `android.net.extra.REM_TETHER_TYPE`, was `extraRemTetherType`
+ActionValue: android.net.TetheringConstants#EXTRA_RUN_PROVISION:
+ Inconsistent extra value; expected `android.net.extra.RUN_PROVISION`, was `extraRunProvision`
+ActionValue: android.net.TetheringConstants#EXTRA_SET_ALARM:
+ Inconsistent extra value; expected `android.net.extra.SET_ALARM`, was `extraSetAlarm`
+ActionValue: android.net.TetheringManager#ACTION_TETHER_STATE_CHANGED:
+ Inconsistent action value; expected `android.net.action.TETHER_STATE_CHANGED`, was `android.net.conn.TETHER_STATE_CHANGED`
+ActionValue: android.net.TetheringManager#EXTRA_ACTIVE_TETHER:
+ Inconsistent extra value; expected `android.net.extra.ACTIVE_TETHER`, was `tetherArray`
+ActionValue: android.net.TetheringManager#EXTRA_AVAILABLE_TETHER:
+ Inconsistent extra value; expected `android.net.extra.AVAILABLE_TETHER`, was `availableArray`
+ActionValue: android.net.TetheringManager#EXTRA_ERRORED_TETHER:
+ Inconsistent extra value; expected `android.net.extra.ERRORED_TETHER`, was `erroredArray`
+
+
+ManagerConstructor: android.net.TetheringManager#TetheringManager(android.content.Context, java.util.function.Supplier<android.os.IBinder>):
+ Managers must always be obtained from Context; no direct constructors
+
+
+PrivateSuperclass: android.location.GnssAntennaInfo.PhaseCenterVariationCorrections:
+ Public class android.location.GnssAntennaInfo.PhaseCenterVariationCorrections extends private class android.location.GnssAntennaInfo.SphericalCorrections
+PrivateSuperclass: android.location.GnssAntennaInfo.SignalGainCorrections:
+ Public class android.location.GnssAntennaInfo.SignalGainCorrections extends private class android.location.GnssAntennaInfo.SphericalCorrections
diff --git a/api/system-current.txt b/api/system-current.txt
index 202d51f..764a386 100755
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -130,6 +130,7 @@
field public static final String NETWORK_FACTORY = "android.permission.NETWORK_FACTORY";
field public static final String NETWORK_MANAGED_PROVISIONING = "android.permission.NETWORK_MANAGED_PROVISIONING";
field public static final String NETWORK_SCAN = "android.permission.NETWORK_SCAN";
+ field public static final String NETWORK_SETTINGS = "android.permission.NETWORK_SETTINGS";
field public static final String NETWORK_SETUP_WIZARD = "android.permission.NETWORK_SETUP_WIZARD";
field public static final String NETWORK_SIGNAL_STRENGTH_WAKEUP = "android.permission.NETWORK_SIGNAL_STRENGTH_WAKEUP";
field public static final String NETWORK_STACK = "android.permission.NETWORK_STACK";
@@ -1448,15 +1449,17 @@
}
public final class BluetoothHidHost implements android.bluetooth.BluetoothProfile {
- method @NonNull public java.util.List<android.bluetooth.BluetoothDevice> getConnectedDevices();
- method @RequiresPermission(android.Manifest.permission.BLUETOOTH) public int getConnectionPolicy(@Nullable android.bluetooth.BluetoothDevice);
- method public int getConnectionState(@Nullable android.bluetooth.BluetoothDevice);
- method @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN) public boolean setConnectionPolicy(@Nullable android.bluetooth.BluetoothDevice, int);
+ method @NonNull @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public java.util.List<android.bluetooth.BluetoothDevice> getConnectedDevices();
+ method @RequiresPermission(android.Manifest.permission.BLUETOOTH) public int getConnectionPolicy(@NonNull android.bluetooth.BluetoothDevice);
+ method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public int getConnectionState(@NonNull android.bluetooth.BluetoothDevice);
+ method @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN) public boolean setConnectionPolicy(@NonNull android.bluetooth.BluetoothDevice, int);
field public static final String ACTION_CONNECTION_STATE_CHANGED = "android.bluetooth.input.profile.action.CONNECTION_STATE_CHANGED";
}
- public final class BluetoothMap implements android.bluetooth.BluetoothProfile {
- method @NonNull public java.util.List<android.bluetooth.BluetoothDevice> getConnectedDevices();
+ public final class BluetoothMap implements java.lang.AutoCloseable android.bluetooth.BluetoothProfile {
+ method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public void close();
+ method @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) protected void finalize();
+ method @NonNull @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) public java.util.List<android.bluetooth.BluetoothDevice> getConnectedDevices();
method @RequiresPermission(android.Manifest.permission.BLUETOOTH) public int getConnectionPolicy(@Nullable android.bluetooth.BluetoothDevice);
method @RequiresPermission(android.Manifest.permission.BLUETOOTH_ADMIN) public boolean setConnectionPolicy(@Nullable android.bluetooth.BluetoothDevice, int);
field public static final String ACTION_CONNECTION_STATE_CHANGED = "android.bluetooth.map.profile.action.CONNECTION_STATE_CHANGED";
@@ -3349,8 +3352,14 @@
method @RequiresPermission(android.Manifest.permission.MANAGE_USB) public void setCurrentFunctions(long);
field @RequiresPermission(android.Manifest.permission.MANAGE_USB) public static final String ACTION_USB_PORT_CHANGED = "android.hardware.usb.action.USB_PORT_CHANGED";
field public static final String ACTION_USB_STATE = "android.hardware.usb.action.USB_STATE";
+ field public static final long FUNCTION_ACCESSORY = 2L; // 0x2L
+ field public static final long FUNCTION_ADB = 1L; // 0x1L
+ field public static final long FUNCTION_AUDIO_SOURCE = 64L; // 0x40L
+ field public static final long FUNCTION_MIDI = 8L; // 0x8L
+ field public static final long FUNCTION_MTP = 4L; // 0x4L
field public static final long FUNCTION_NCM = 1024L; // 0x400L
field public static final long FUNCTION_NONE = 0L; // 0x0L
+ field public static final long FUNCTION_PTP = 16L; // 0x10L
field public static final long FUNCTION_RNDIS = 32L; // 0x20L
field public static final String USB_CONFIGURED = "configured";
field public static final String USB_CONNECTED = "connected";
@@ -4405,13 +4414,13 @@
public class ConnectivityManager {
method @NonNull @RequiresPermission(android.Manifest.permission.PACKET_KEEPALIVE_OFFLOAD) public android.net.SocketKeepalive createNattKeepalive(@NonNull android.net.Network, @NonNull android.os.ParcelFileDescriptor, @NonNull java.net.InetAddress, @NonNull java.net.InetAddress, @NonNull java.util.concurrent.Executor, @NonNull android.net.SocketKeepalive.Callback);
method @NonNull @RequiresPermission(android.Manifest.permission.PACKET_KEEPALIVE_OFFLOAD) public android.net.SocketKeepalive createSocketKeepalive(@NonNull android.net.Network, @NonNull java.net.Socket, @NonNull java.util.concurrent.Executor, @NonNull android.net.SocketKeepalive.Callback);
- method @Deprecated @RequiresPermission("android.permission.NETWORK_SETTINGS") public String getCaptivePortalServerUrl();
+ method @Deprecated @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public String getCaptivePortalServerUrl();
method @Deprecated @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void getLatestTetheringEntitlementResult(int, boolean, @NonNull java.util.concurrent.Executor, @NonNull android.net.ConnectivityManager.OnTetheringEntitlementResultListener);
method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.TETHER_PRIVILEGED, android.Manifest.permission.WRITE_SETTINGS}) public boolean isTetheringSupported();
method @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY) public int registerNetworkProvider(@NonNull android.net.NetworkProvider);
method @Deprecated @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void registerTetheringEventCallback(@NonNull java.util.concurrent.Executor, @NonNull android.net.ConnectivityManager.OnTetheringEventCallback);
method @Deprecated public void requestNetwork(@NonNull android.net.NetworkRequest, @NonNull android.net.ConnectivityManager.NetworkCallback, int, int, @NonNull android.os.Handler);
- method @RequiresPermission(anyOf={"android.permission.NETWORK_SETTINGS", android.Manifest.permission.NETWORK_SETUP_WIZARD, android.Manifest.permission.NETWORK_STACK}) public void setAirplaneMode(boolean);
+ method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD, android.Manifest.permission.NETWORK_STACK}) public void setAirplaneMode(boolean);
method @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_STACK}) public boolean shouldAvoidBadWifi();
method @RequiresPermission(android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK) public void startCaptivePortalApp(@NonNull android.net.Network, @NonNull android.os.Bundle);
method @Deprecated @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void startTethering(int, boolean, android.net.ConnectivityManager.OnStartTetheringCallback);
@@ -4447,7 +4456,7 @@
}
public class EthernetManager {
- method @NonNull public android.net.EthernetManager.TetheredInterfaceRequest requestTetheredInterface(@NonNull android.net.EthernetManager.TetheredInterfaceCallback);
+ method @NonNull public android.net.EthernetManager.TetheredInterfaceRequest requestTetheredInterface(@NonNull java.util.concurrent.Executor, @NonNull android.net.EthernetManager.TetheredInterfaceCallback);
}
public static interface EthernetManager.TetheredInterfaceCallback {
@@ -4616,6 +4625,10 @@
method public void sendNetworkCapabilities(@NonNull android.net.NetworkCapabilities);
method public void sendNetworkScore(int);
method public void sendSocketKeepaliveEvent(int, int);
+ method public void setConnected();
+ method @Deprecated public void setLegacyExtraInfo(@Nullable String);
+ method @Deprecated public void setLegacySubtype(int, @NonNull String);
+ method public void unregister();
field public static final int VALIDATION_STATUS_NOT_VALID = 2; // 0x2
field public static final int VALIDATION_STATUS_VALID = 1; // 0x1
field @NonNull public final android.net.Network network;
@@ -4994,337 +5007,6 @@
}
-package android.net.eap {
-
- public final class EapSessionConfig {
- }
-
- public static final class EapSessionConfig.Builder {
- ctor public EapSessionConfig.Builder();
- method @NonNull public android.net.eap.EapSessionConfig build();
- method @NonNull public android.net.eap.EapSessionConfig.Builder setEapAkaConfig(int, int);
- method @NonNull public android.net.eap.EapSessionConfig.Builder setEapAkaPrimeConfig(int, int, @NonNull String, boolean);
- method @NonNull public android.net.eap.EapSessionConfig.Builder setEapIdentity(@NonNull byte[]);
- method @NonNull public android.net.eap.EapSessionConfig.Builder setEapMsChapV2Config(@NonNull String, @NonNull String);
- method @NonNull public android.net.eap.EapSessionConfig.Builder setEapSimConfig(int, int);
- }
-
- public static class EapSessionConfig.EapAkaConfig extends android.net.eap.EapSessionConfig.EapUiccConfig {
- }
-
- public static class EapSessionConfig.EapAkaPrimeConfig extends android.net.eap.EapSessionConfig.EapAkaConfig {
- method public boolean allowsMismatchedNetworkNames();
- method @NonNull public String getNetworkName();
- }
-
- public abstract static class EapSessionConfig.EapMethodConfig {
- method public int getMethodType();
- }
-
- public static class EapSessionConfig.EapMsChapV2Config extends android.net.eap.EapSessionConfig.EapMethodConfig {
- method @NonNull public String getPassword();
- method @NonNull public String getUsername();
- }
-
- public static class EapSessionConfig.EapSimConfig extends android.net.eap.EapSessionConfig.EapUiccConfig {
- }
-
- public abstract static class EapSessionConfig.EapUiccConfig extends android.net.eap.EapSessionConfig.EapMethodConfig {
- method public int getAppType();
- method public int getSubId();
- }
-
-}
-
-package android.net.ipsec.ike {
-
- public final class ChildSaProposal extends android.net.ipsec.ike.SaProposal {
- }
-
- public static final class ChildSaProposal.Builder {
- ctor public ChildSaProposal.Builder();
- method @NonNull public android.net.ipsec.ike.ChildSaProposal.Builder addDhGroup(int);
- method @NonNull public android.net.ipsec.ike.ChildSaProposal.Builder addEncryptionAlgorithm(int, int);
- method @NonNull public android.net.ipsec.ike.ChildSaProposal.Builder addIntegrityAlgorithm(int);
- method @NonNull public android.net.ipsec.ike.ChildSaProposal build();
- }
-
- public interface ChildSessionCallback {
- method public void onClosed();
- method public void onClosedExceptionally(@NonNull android.net.ipsec.ike.exceptions.IkeException);
- method public void onIpSecTransformCreated(@NonNull android.net.IpSecTransform, int);
- method public void onIpSecTransformDeleted(@NonNull android.net.IpSecTransform, int);
- method public void onOpened(@NonNull android.net.ipsec.ike.ChildSessionConfiguration);
- }
-
- public final class ChildSessionConfiguration {
- method @NonNull public java.util.List<android.net.ipsec.ike.IkeTrafficSelector> getInboundTrafficSelectors();
- method @NonNull public java.util.List<android.net.LinkAddress> getInternalAddresses();
- method @NonNull public java.util.List<java.net.InetAddress> getInternalDhcpServers();
- method @NonNull public java.util.List<java.net.InetAddress> getInternalDnsServers();
- method @NonNull public java.util.List<android.net.IpPrefix> getInternalSubnets();
- method @NonNull public java.util.List<android.net.ipsec.ike.IkeTrafficSelector> getOutboundTrafficSelectors();
- }
-
- public abstract class ChildSessionParams {
- method public long getHardLifetime();
- method @NonNull public java.util.List<android.net.ipsec.ike.IkeTrafficSelector> getLocalTrafficSelectors();
- method @NonNull public java.util.List<android.net.ipsec.ike.IkeTrafficSelector> getRemoteTrafficSelectors();
- method @NonNull public java.util.List<android.net.ipsec.ike.ChildSaProposal> getSaProposals();
- method public long getSoftLifetime();
- }
-
- public class IkeFqdnIdentification extends android.net.ipsec.ike.IkeIdentification {
- ctor public IkeFqdnIdentification(@NonNull String);
- field @NonNull public final String fqdn;
- }
-
- public abstract class IkeIdentification {
- }
-
- public final class IkeIpv4AddrIdentification extends android.net.ipsec.ike.IkeIdentification {
- ctor public IkeIpv4AddrIdentification(@NonNull java.net.Inet4Address);
- field @NonNull public final java.net.Inet4Address ipv4Address;
- }
-
- public class IkeIpv6AddrIdentification extends android.net.ipsec.ike.IkeIdentification {
- ctor public IkeIpv6AddrIdentification(@NonNull java.net.Inet6Address);
- field @NonNull public final java.net.Inet6Address ipv6Address;
- }
-
- public final class IkeKeyIdIdentification extends android.net.ipsec.ike.IkeIdentification {
- ctor public IkeKeyIdIdentification(@NonNull byte[]);
- field @NonNull public final byte[] keyId;
- }
-
- public final class IkeRfc822AddrIdentification extends android.net.ipsec.ike.IkeIdentification {
- ctor public IkeRfc822AddrIdentification(@NonNull String);
- field @NonNull public final String rfc822Name;
- }
-
- public final class IkeSaProposal extends android.net.ipsec.ike.SaProposal {
- method @NonNull public java.util.List<java.lang.Integer> getPseudorandomFunctions();
- }
-
- public static final class IkeSaProposal.Builder {
- ctor public IkeSaProposal.Builder();
- method @NonNull public android.net.ipsec.ike.IkeSaProposal.Builder addDhGroup(int);
- method @NonNull public android.net.ipsec.ike.IkeSaProposal.Builder addEncryptionAlgorithm(int, int);
- method @NonNull public android.net.ipsec.ike.IkeSaProposal.Builder addIntegrityAlgorithm(int);
- method @NonNull public android.net.ipsec.ike.IkeSaProposal.Builder addPseudorandomFunction(int);
- method @NonNull public android.net.ipsec.ike.IkeSaProposal build();
- }
-
- public final class IkeSession implements java.lang.AutoCloseable {
- ctor public IkeSession(@NonNull android.content.Context, @NonNull android.net.ipsec.ike.IkeSessionParams, @NonNull android.net.ipsec.ike.ChildSessionParams, @NonNull java.util.concurrent.Executor, @NonNull android.net.ipsec.ike.IkeSessionCallback, @NonNull android.net.ipsec.ike.ChildSessionCallback);
- method public void close();
- method public void closeChildSession(@NonNull android.net.ipsec.ike.ChildSessionCallback);
- method public void kill();
- method public void openChildSession(@NonNull android.net.ipsec.ike.ChildSessionParams, @NonNull android.net.ipsec.ike.ChildSessionCallback);
- }
-
- public interface IkeSessionCallback {
- method public void onClosed();
- method public void onClosedExceptionally(@NonNull android.net.ipsec.ike.exceptions.IkeException);
- method public void onError(@NonNull android.net.ipsec.ike.exceptions.IkeProtocolException);
- method public void onOpened(@NonNull android.net.ipsec.ike.IkeSessionConfiguration);
- }
-
- public final class IkeSessionConfiguration {
- method @NonNull public java.util.List<java.net.InetAddress> getPcscfServers();
- method @NonNull public String getRemoteApplicationVersion();
- method @NonNull public java.util.List<byte[]> getRemoteVendorIDs();
- method public boolean isIkeExtensionEnabled(int);
- field public static final int EXTENSION_TYPE_FRAGMENTATION = 1; // 0x1
- field public static final int EXTENSION_TYPE_MOBIKE = 2; // 0x2
- }
-
- public final class IkeSessionParams {
- method @NonNull public java.util.List<android.net.ipsec.ike.IkeSessionParams.IkeConfigRequest> getConfigurationRequests();
- method public long getHardLifetime();
- method @NonNull public android.net.ipsec.ike.IkeSessionParams.IkeAuthConfig getLocalAuthConfig();
- method @NonNull public android.net.ipsec.ike.IkeIdentification getLocalIdentification();
- method @NonNull public android.net.ipsec.ike.IkeSessionParams.IkeAuthConfig getRemoteAuthConfig();
- method @NonNull public android.net.ipsec.ike.IkeIdentification getRemoteIdentification();
- method @NonNull public java.util.List<android.net.ipsec.ike.IkeSaProposal> getSaProposals();
- method @NonNull public java.net.InetAddress getServerAddress();
- method public long getSoftLifetime();
- method @NonNull public android.net.IpSecManager.UdpEncapsulationSocket getUdpEncapsulationSocket();
- }
-
- public static final class IkeSessionParams.Builder {
- ctor public IkeSessionParams.Builder();
- method @NonNull public android.net.ipsec.ike.IkeSessionParams.Builder addPcscfServerRequest(@NonNull java.net.InetAddress);
- method @NonNull public android.net.ipsec.ike.IkeSessionParams.Builder addPcscfServerRequest(int);
- method @NonNull public android.net.ipsec.ike.IkeSessionParams.Builder addSaProposal(@NonNull android.net.ipsec.ike.IkeSaProposal);
- method @NonNull public android.net.ipsec.ike.IkeSessionParams build();
- method @NonNull public android.net.ipsec.ike.IkeSessionParams.Builder setAuthDigitalSignature(@Nullable java.security.cert.X509Certificate, @NonNull java.security.cert.X509Certificate, @NonNull java.security.PrivateKey);
- method @NonNull public android.net.ipsec.ike.IkeSessionParams.Builder setAuthDigitalSignature(@Nullable java.security.cert.X509Certificate, @NonNull java.security.cert.X509Certificate, @NonNull java.util.List<java.security.cert.X509Certificate>, @NonNull java.security.PrivateKey);
- method @NonNull public android.net.ipsec.ike.IkeSessionParams.Builder setAuthEap(@Nullable java.security.cert.X509Certificate, @NonNull android.net.eap.EapSessionConfig);
- method @NonNull public android.net.ipsec.ike.IkeSessionParams.Builder setAuthPsk(@NonNull byte[]);
- method @NonNull public android.net.ipsec.ike.IkeSessionParams.Builder setLifetime(long, long);
- method @NonNull public android.net.ipsec.ike.IkeSessionParams.Builder setLocalIdentification(@NonNull android.net.ipsec.ike.IkeIdentification);
- method @NonNull public android.net.ipsec.ike.IkeSessionParams.Builder setRemoteIdentification(@NonNull android.net.ipsec.ike.IkeIdentification);
- method @NonNull public android.net.ipsec.ike.IkeSessionParams.Builder setServerAddress(@NonNull java.net.InetAddress);
- method @NonNull public android.net.ipsec.ike.IkeSessionParams.Builder setUdpEncapsulationSocket(@NonNull android.net.IpSecManager.UdpEncapsulationSocket);
- }
-
- public static interface IkeSessionParams.ConfigRequestIpv4PcscfServer extends android.net.ipsec.ike.IkeSessionParams.IkeConfigRequest {
- method @Nullable public java.net.Inet4Address getAddress();
- }
-
- public static interface IkeSessionParams.ConfigRequestIpv6PcscfServer extends android.net.ipsec.ike.IkeSessionParams.IkeConfigRequest {
- method @Nullable public java.net.Inet6Address getAddress();
- }
-
- public abstract static class IkeSessionParams.IkeAuthConfig {
- }
-
- public static class IkeSessionParams.IkeAuthDigitalSignLocalConfig extends android.net.ipsec.ike.IkeSessionParams.IkeAuthConfig {
- method @NonNull public java.security.cert.X509Certificate getClientEndCertificate();
- method @NonNull public java.util.List<java.security.cert.X509Certificate> getIntermediateCertificates();
- method @NonNull public java.security.PrivateKey getPrivateKey();
- }
-
- public static class IkeSessionParams.IkeAuthDigitalSignRemoteConfig extends android.net.ipsec.ike.IkeSessionParams.IkeAuthConfig {
- method @Nullable public java.security.cert.X509Certificate getRemoteCaCert();
- }
-
- public static class IkeSessionParams.IkeAuthEapConfig extends android.net.ipsec.ike.IkeSessionParams.IkeAuthConfig {
- method @NonNull public android.net.eap.EapSessionConfig getEapConfig();
- }
-
- public static class IkeSessionParams.IkeAuthPskConfig extends android.net.ipsec.ike.IkeSessionParams.IkeAuthConfig {
- method @NonNull public byte[] getPsk();
- }
-
- public static interface IkeSessionParams.IkeConfigRequest {
- }
-
- public final class IkeTrafficSelector {
- ctor public IkeTrafficSelector(int, int, @NonNull java.net.InetAddress, @NonNull java.net.InetAddress);
- field public final int endPort;
- field @NonNull public final java.net.InetAddress endingAddress;
- field public final int startPort;
- field @NonNull public final java.net.InetAddress startingAddress;
- }
-
- public abstract class SaProposal {
- method @NonNull public java.util.List<java.lang.Integer> getDhGroups();
- method @NonNull public java.util.List<android.util.Pair<java.lang.Integer,java.lang.Integer>> getEncryptionAlgorithms();
- method @NonNull public java.util.List<java.lang.Integer> getIntegrityAlgorithms();
- field public static final int DH_GROUP_1024_BIT_MODP = 2; // 0x2
- field public static final int DH_GROUP_2048_BIT_MODP = 14; // 0xe
- field public static final int DH_GROUP_NONE = 0; // 0x0
- field public static final int ENCRYPTION_ALGORITHM_3DES = 3; // 0x3
- field public static final int ENCRYPTION_ALGORITHM_AES_CBC = 12; // 0xc
- field public static final int ENCRYPTION_ALGORITHM_AES_GCM_12 = 19; // 0x13
- field public static final int ENCRYPTION_ALGORITHM_AES_GCM_16 = 20; // 0x14
- field public static final int ENCRYPTION_ALGORITHM_AES_GCM_8 = 18; // 0x12
- field public static final int INTEGRITY_ALGORITHM_AES_XCBC_96 = 5; // 0x5
- field public static final int INTEGRITY_ALGORITHM_HMAC_SHA1_96 = 2; // 0x2
- field public static final int INTEGRITY_ALGORITHM_HMAC_SHA2_256_128 = 12; // 0xc
- field public static final int INTEGRITY_ALGORITHM_HMAC_SHA2_384_192 = 13; // 0xd
- field public static final int INTEGRITY_ALGORITHM_HMAC_SHA2_512_256 = 14; // 0xe
- field public static final int INTEGRITY_ALGORITHM_NONE = 0; // 0x0
- field public static final int KEY_LEN_AES_128 = 128; // 0x80
- field public static final int KEY_LEN_AES_192 = 192; // 0xc0
- field public static final int KEY_LEN_AES_256 = 256; // 0x100
- field public static final int KEY_LEN_UNUSED = 0; // 0x0
- field public static final int PSEUDORANDOM_FUNCTION_AES128_XCBC = 4; // 0x4
- field public static final int PSEUDORANDOM_FUNCTION_HMAC_SHA1 = 2; // 0x2
- }
-
- public final class TransportModeChildSessionParams extends android.net.ipsec.ike.ChildSessionParams {
- }
-
- public static final class TransportModeChildSessionParams.Builder {
- ctor public TransportModeChildSessionParams.Builder();
- method @NonNull public android.net.ipsec.ike.TransportModeChildSessionParams.Builder addInboundTrafficSelectors(@NonNull android.net.ipsec.ike.IkeTrafficSelector);
- method @NonNull public android.net.ipsec.ike.TransportModeChildSessionParams.Builder addOutboundTrafficSelectors(@NonNull android.net.ipsec.ike.IkeTrafficSelector);
- method @NonNull public android.net.ipsec.ike.TransportModeChildSessionParams.Builder addSaProposal(@NonNull android.net.ipsec.ike.ChildSaProposal);
- method @NonNull public android.net.ipsec.ike.TransportModeChildSessionParams build();
- method @NonNull public android.net.ipsec.ike.TransportModeChildSessionParams.Builder setLifetime(long, long);
- }
-
- public final class TunnelModeChildSessionParams extends android.net.ipsec.ike.ChildSessionParams {
- method @NonNull public java.util.List<android.net.ipsec.ike.TunnelModeChildSessionParams.TunnelModeChildConfigRequest> getConfigurationRequests();
- }
-
- public static final class TunnelModeChildSessionParams.Builder {
- ctor public TunnelModeChildSessionParams.Builder();
- method @NonNull public android.net.ipsec.ike.TunnelModeChildSessionParams.Builder addInboundTrafficSelectors(@NonNull android.net.ipsec.ike.IkeTrafficSelector);
- method @NonNull public android.net.ipsec.ike.TunnelModeChildSessionParams.Builder addInternalAddressRequest(int);
- method @NonNull public android.net.ipsec.ike.TunnelModeChildSessionParams.Builder addInternalAddressRequest(@NonNull java.net.Inet4Address);
- method @NonNull public android.net.ipsec.ike.TunnelModeChildSessionParams.Builder addInternalAddressRequest(@NonNull java.net.Inet6Address, int);
- method @NonNull public android.net.ipsec.ike.TunnelModeChildSessionParams.Builder addInternalDhcpServerRequest(int);
- method @NonNull public android.net.ipsec.ike.TunnelModeChildSessionParams.Builder addInternalDnsServerRequest(int);
- method @NonNull public android.net.ipsec.ike.TunnelModeChildSessionParams.Builder addOutboundTrafficSelectors(@NonNull android.net.ipsec.ike.IkeTrafficSelector);
- method @NonNull public android.net.ipsec.ike.TunnelModeChildSessionParams.Builder addSaProposal(@NonNull android.net.ipsec.ike.ChildSaProposal);
- method @NonNull public android.net.ipsec.ike.TunnelModeChildSessionParams build();
- method @NonNull public android.net.ipsec.ike.TunnelModeChildSessionParams.Builder setLifetime(long, long);
- }
-
- public static interface TunnelModeChildSessionParams.ConfigRequestIpv4Address extends android.net.ipsec.ike.TunnelModeChildSessionParams.TunnelModeChildConfigRequest {
- method @Nullable public java.net.Inet4Address getAddress();
- }
-
- public static interface TunnelModeChildSessionParams.ConfigRequestIpv4DhcpServer extends android.net.ipsec.ike.TunnelModeChildSessionParams.TunnelModeChildConfigRequest {
- method @Nullable public java.net.Inet4Address getAddress();
- }
-
- public static interface TunnelModeChildSessionParams.ConfigRequestIpv4DnsServer extends android.net.ipsec.ike.TunnelModeChildSessionParams.TunnelModeChildConfigRequest {
- method @Nullable public java.net.Inet4Address getAddress();
- }
-
- public static interface TunnelModeChildSessionParams.ConfigRequestIpv4Netmask extends android.net.ipsec.ike.TunnelModeChildSessionParams.TunnelModeChildConfigRequest {
- }
-
- public static interface TunnelModeChildSessionParams.ConfigRequestIpv6Address extends android.net.ipsec.ike.TunnelModeChildSessionParams.TunnelModeChildConfigRequest {
- method @Nullable public java.net.Inet6Address getAddress();
- method public int getPrefixLength();
- }
-
- public static interface TunnelModeChildSessionParams.ConfigRequestIpv6DnsServer extends android.net.ipsec.ike.TunnelModeChildSessionParams.TunnelModeChildConfigRequest {
- method @Nullable public java.net.Inet6Address getAddress();
- }
-
- public static interface TunnelModeChildSessionParams.TunnelModeChildConfigRequest {
- }
-
-}
-
-package android.net.ipsec.ike.exceptions {
-
- public abstract class IkeException extends java.lang.Exception {
- }
-
- public final class IkeInternalException extends android.net.ipsec.ike.exceptions.IkeException {
- }
-
- public abstract class IkeProtocolException extends android.net.ipsec.ike.exceptions.IkeException {
- method @Nullable public byte[] getErrorData();
- method public int getErrorType();
- field public static final int ERROR_TYPE_AUTHENTICATION_FAILED = 24; // 0x18
- field public static final int ERROR_TYPE_CHILD_SA_NOT_FOUND = 44; // 0x2c
- field public static final int ERROR_TYPE_FAILED_CP_REQUIRED = 37; // 0x25
- field public static final int ERROR_TYPE_INTERNAL_ADDRESS_FAILURE = 36; // 0x24
- field public static final int ERROR_TYPE_INVALID_IKE_SPI = 4; // 0x4
- field public static final int ERROR_TYPE_INVALID_KE_PAYLOAD = 17; // 0x11
- field public static final int ERROR_TYPE_INVALID_MAJOR_VERSION = 5; // 0x5
- field public static final int ERROR_TYPE_INVALID_MESSAGE_ID = 9; // 0x9
- field public static final int ERROR_TYPE_INVALID_SELECTORS = 39; // 0x27
- field public static final int ERROR_TYPE_INVALID_SYNTAX = 7; // 0x7
- field public static final int ERROR_TYPE_NO_ADDITIONAL_SAS = 35; // 0x23
- field public static final int ERROR_TYPE_NO_PROPOSAL_CHOSEN = 14; // 0xe
- field public static final int ERROR_TYPE_SINGLE_PAIR_REQUIRED = 34; // 0x22
- field public static final int ERROR_TYPE_TEMPORARY_FAILURE = 43; // 0x2b
- field public static final int ERROR_TYPE_TS_UNACCEPTABLE = 38; // 0x26
- field public static final int ERROR_TYPE_UNSUPPORTED_CRITICAL_PAYLOAD = 1; // 0x1
- }
-
-}
-
package android.net.metrics {
public final class ApfProgramEvent implements android.net.metrics.IpConnectivityLog.Event {
@@ -5784,13 +5466,13 @@
public class WifiManager {
method @RequiresPermission("android.permission.WIFI_UPDATE_USABILITY_STATS_SCORE") public void addOnWifiUsabilityStatsListener(@NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.WifiManager.OnWifiUsabilityStatsListener);
- method @RequiresPermission(anyOf={"android.permission.NETWORK_SETTINGS", android.Manifest.permission.NETWORK_SETUP_WIZARD, android.Manifest.permission.NETWORK_STACK}) public void connect(@NonNull android.net.wifi.WifiConfiguration, @Nullable android.net.wifi.WifiManager.ActionListener);
- method @RequiresPermission(anyOf={"android.permission.NETWORK_SETTINGS", android.Manifest.permission.NETWORK_SETUP_WIZARD, android.Manifest.permission.NETWORK_STACK}) public void connect(int, @Nullable android.net.wifi.WifiManager.ActionListener);
- method @RequiresPermission(anyOf={"android.permission.NETWORK_SETTINGS", android.Manifest.permission.NETWORK_SETUP_WIZARD, android.Manifest.permission.NETWORK_STACK}) public void disable(int, @Nullable android.net.wifi.WifiManager.ActionListener);
- method @RequiresPermission(anyOf={"android.permission.NETWORK_SETTINGS", android.Manifest.permission.NETWORK_SETUP_WIZARD, android.Manifest.permission.NETWORK_STACK}) public void forget(int, @Nullable android.net.wifi.WifiManager.ActionListener);
- method @NonNull @RequiresPermission(anyOf={"android.permission.NETWORK_SETTINGS", android.Manifest.permission.NETWORK_SETUP_WIZARD}) public java.util.List<android.util.Pair<android.net.wifi.WifiConfiguration,java.util.Map<java.lang.Integer,java.util.List<android.net.wifi.ScanResult>>>> getAllMatchingWifiConfigs(@NonNull java.util.List<android.net.wifi.ScanResult>);
- method @NonNull @RequiresPermission(anyOf={"android.permission.NETWORK_SETTINGS", android.Manifest.permission.NETWORK_SETUP_WIZARD}) public java.util.Map<android.net.wifi.hotspot2.OsuProvider,java.util.List<android.net.wifi.ScanResult>> getMatchingOsuProviders(@Nullable java.util.List<android.net.wifi.ScanResult>);
- method @NonNull @RequiresPermission(anyOf={"android.permission.NETWORK_SETTINGS", android.Manifest.permission.NETWORK_SETUP_WIZARD}) public java.util.Map<android.net.wifi.hotspot2.OsuProvider,android.net.wifi.hotspot2.PasspointConfiguration> getMatchingPasspointConfigsForOsuProviders(@NonNull java.util.Set<android.net.wifi.hotspot2.OsuProvider>);
+ method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD, android.Manifest.permission.NETWORK_STACK}) public void connect(@NonNull android.net.wifi.WifiConfiguration, @Nullable android.net.wifi.WifiManager.ActionListener);
+ method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD, android.Manifest.permission.NETWORK_STACK}) public void connect(int, @Nullable android.net.wifi.WifiManager.ActionListener);
+ method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD, android.Manifest.permission.NETWORK_STACK}) public void disable(int, @Nullable android.net.wifi.WifiManager.ActionListener);
+ method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD, android.Manifest.permission.NETWORK_STACK}) public void forget(int, @Nullable android.net.wifi.WifiManager.ActionListener);
+ method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD}) public java.util.List<android.util.Pair<android.net.wifi.WifiConfiguration,java.util.Map<java.lang.Integer,java.util.List<android.net.wifi.ScanResult>>>> getAllMatchingWifiConfigs(@NonNull java.util.List<android.net.wifi.ScanResult>);
+ method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD}) public java.util.Map<android.net.wifi.hotspot2.OsuProvider,java.util.List<android.net.wifi.ScanResult>> getMatchingOsuProviders(@Nullable java.util.List<android.net.wifi.ScanResult>);
+ method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD}) public java.util.Map<android.net.wifi.hotspot2.OsuProvider,android.net.wifi.hotspot2.PasspointConfiguration> getMatchingPasspointConfigsForOsuProviders(@NonNull java.util.Set<android.net.wifi.hotspot2.OsuProvider>);
method @RequiresPermission(allOf={android.Manifest.permission.ACCESS_FINE_LOCATION, android.Manifest.permission.ACCESS_WIFI_STATE, android.Manifest.permission.READ_WIFI_CREDENTIAL}) public java.util.List<android.net.wifi.WifiConfiguration> getPrivilegedConfiguredNetworks();
method @RequiresPermission(android.Manifest.permission.ACCESS_WIFI_STATE) public android.net.wifi.WifiConfiguration getWifiApConfiguration();
method @RequiresPermission(android.Manifest.permission.ACCESS_WIFI_STATE) public int getWifiApState();
@@ -5798,17 +5480,17 @@
method public boolean isPortableHotspotSupported();
method @RequiresPermission(android.Manifest.permission.ACCESS_WIFI_STATE) public boolean isWifiApEnabled();
method public boolean isWifiScannerSupported();
- method @RequiresPermission("android.permission.NETWORK_SETTINGS") public void registerSoftApCallback(@NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.WifiManager.SoftApCallback);
+ method @RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS) public void registerSoftApCallback(@NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.WifiManager.SoftApCallback);
method @RequiresPermission("android.permission.WIFI_UPDATE_USABILITY_STATS_SCORE") public void removeOnWifiUsabilityStatsListener(@NonNull android.net.wifi.WifiManager.OnWifiUsabilityStatsListener);
- method @RequiresPermission(anyOf={"android.permission.NETWORK_SETTINGS", android.Manifest.permission.NETWORK_SETUP_WIZARD, android.Manifest.permission.NETWORK_STACK}) public void save(@NonNull android.net.wifi.WifiConfiguration, @Nullable android.net.wifi.WifiManager.ActionListener);
+ method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD, android.Manifest.permission.NETWORK_STACK}) public void save(@NonNull android.net.wifi.WifiConfiguration, @Nullable android.net.wifi.WifiManager.ActionListener);
method @RequiresPermission("android.permission.WIFI_SET_DEVICE_MOBILITY_STATE") public void setDeviceMobilityState(int);
method @RequiresPermission(android.Manifest.permission.CHANGE_WIFI_STATE) public boolean setWifiApConfiguration(android.net.wifi.WifiConfiguration);
- method @RequiresPermission(anyOf={"android.permission.NETWORK_SETTINGS", android.Manifest.permission.NETWORK_SETUP_WIZARD}) public void startEasyConnectAsConfiguratorInitiator(@NonNull String, int, int, @NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.EasyConnectStatusCallback);
- method @RequiresPermission(anyOf={"android.permission.NETWORK_SETTINGS", android.Manifest.permission.NETWORK_SETUP_WIZARD}) public void startEasyConnectAsEnrolleeInitiator(@NonNull String, @NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.EasyConnectStatusCallback);
+ method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD}) public void startEasyConnectAsConfiguratorInitiator(@NonNull String, int, int, @NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.EasyConnectStatusCallback);
+ method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD}) public void startEasyConnectAsEnrolleeInitiator(@NonNull String, @NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.EasyConnectStatusCallback);
method @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public boolean startScan(android.os.WorkSource);
method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_STACK, android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK}) public boolean startSoftAp(@Nullable android.net.wifi.WifiConfiguration);
- method @RequiresPermission(anyOf={"android.permission.NETWORK_SETTINGS", android.Manifest.permission.NETWORK_SETUP_WIZARD}) public void startSubscriptionProvisioning(@NonNull android.net.wifi.hotspot2.OsuProvider, @NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.hotspot2.ProvisioningCallback);
- method @RequiresPermission(anyOf={"android.permission.NETWORK_SETTINGS", android.Manifest.permission.NETWORK_SETUP_WIZARD}) public void stopEasyConnectSession();
+ method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD}) public void startSubscriptionProvisioning(@NonNull android.net.wifi.hotspot2.OsuProvider, @NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.hotspot2.ProvisioningCallback);
+ method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_SETTINGS, android.Manifest.permission.NETWORK_SETUP_WIZARD}) public void stopEasyConnectSession();
method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_STACK, android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK}) public boolean stopSoftAp();
method @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_STACK, android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK}) public void updateInterfaceIpState(@Nullable String, int);
method @RequiresPermission("android.permission.WIFI_UPDATE_USABILITY_STATS_SCORE") public void updateWifiUsabilityScore(int, int, int);
@@ -7692,28 +7374,6 @@
method @NonNull @WorkerThread public abstract java.util.List<android.content.ContentValues> onRestoreApns(int);
}
- public abstract class CarrierMessagingServiceWrapper {
- ctor public CarrierMessagingServiceWrapper();
- method public boolean bindToCarrierMessagingService(@NonNull android.content.Context, @NonNull String);
- method public void disposeConnection(@NonNull android.content.Context);
- method public void downloadMms(@NonNull android.net.Uri, int, @NonNull android.net.Uri, @NonNull android.service.carrier.CarrierMessagingServiceWrapper.CarrierMessagingCallbackWrapper);
- method public void filterSms(@NonNull android.service.carrier.MessagePdu, @NonNull String, int, int, @NonNull android.service.carrier.CarrierMessagingServiceWrapper.CarrierMessagingCallbackWrapper);
- method public abstract void onServiceReady();
- method public void sendDataSms(@NonNull byte[], int, @NonNull String, int, int, @NonNull android.service.carrier.CarrierMessagingServiceWrapper.CarrierMessagingCallbackWrapper);
- method public void sendMms(@NonNull android.net.Uri, int, @NonNull android.net.Uri, @NonNull android.service.carrier.CarrierMessagingServiceWrapper.CarrierMessagingCallbackWrapper);
- method public void sendMultipartTextSms(@NonNull java.util.List<java.lang.String>, int, @NonNull String, int, @NonNull android.service.carrier.CarrierMessagingServiceWrapper.CarrierMessagingCallbackWrapper);
- method public void sendTextSms(@NonNull String, int, @NonNull String, int, @NonNull android.service.carrier.CarrierMessagingServiceWrapper.CarrierMessagingCallbackWrapper);
- }
-
- public abstract static class CarrierMessagingServiceWrapper.CarrierMessagingCallbackWrapper {
- ctor public CarrierMessagingServiceWrapper.CarrierMessagingCallbackWrapper();
- method public void onDownloadMmsComplete(int);
- method public void onFilterComplete(int);
- method public void onSendMmsComplete(int, @Nullable byte[]);
- method public void onSendMultipartSmsComplete(int, @Nullable int[]);
- method public void onSendSmsComplete(int, int);
- }
-
}
package android.service.contentcapture {
@@ -9416,6 +9076,7 @@
method public void fillInNotifierBundle(@NonNull android.os.Bundle);
method public int getDataNetworkType();
method public int getDataRegistrationState();
+ method public boolean getDataRoamingFromRegistration();
method @Nullable public android.telephony.NetworkRegistrationInfo getNetworkRegistrationInfo(int, int);
method @NonNull public java.util.List<android.telephony.NetworkRegistrationInfo> getNetworkRegistrationInfoListForDomain(int);
method @NonNull public java.util.List<android.telephony.NetworkRegistrationInfo> getNetworkRegistrationInfoListForTransportType(int);
@@ -9583,6 +9244,8 @@
public class SubscriptionManager {
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean canDisablePhysicalSubscription();
method public boolean canManageSubscription(@Nullable android.telephony.SubscriptionInfo, @Nullable String);
+ method @NonNull public int[] getActiveAndHiddenSubscriptionIdList();
+ method @NonNull public int[] getActiveSubscriptionIdList();
method @Nullable @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public android.telephony.SubscriptionInfo getActiveSubscriptionInfoForIcc(@NonNull String);
method public java.util.List<android.telephony.SubscriptionInfo> getAvailableSubscriptionInfoList();
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getEnabledSubscriptionId(int);
@@ -9707,6 +9370,7 @@
method public boolean isDataConnectivityPossible();
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isDataEnabledForApn(int);
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isEmergencyAssistanceEnabled();
+ method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isGlobalModeEnabled();
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) @WorkerThread public boolean isIccLockEnabled();
method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, android.Manifest.permission.READ_PHONE_STATE}) public boolean isIdle();
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public boolean isInEmergencySmsMode();
@@ -9868,16 +9532,6 @@
field public static final int SRVCC_STATE_HANDOVER_STARTED = 0; // 0x0
}
- public class TelephonyRegistryManager {
- method public void addOnOpportunisticSubscriptionsChangedListener(@NonNull android.telephony.SubscriptionManager.OnOpportunisticSubscriptionsChangedListener, @NonNull java.util.concurrent.Executor);
- method public void addOnSubscriptionsChangedListener(@NonNull android.telephony.SubscriptionManager.OnSubscriptionsChangedListener, @NonNull java.util.concurrent.Executor);
- method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void notifyCallStateChangedForAllSubscriptions(int, @Nullable String);
- method public void notifyCarrierNetworkChange(boolean);
- method public void notifyRegistrationFailed(int, int, @NonNull android.telephony.CellIdentity, @NonNull String, int, int, int);
- method public void removeOnOpportunisticSubscriptionsChangedListener(@NonNull android.telephony.SubscriptionManager.OnOpportunisticSubscriptionsChangedListener);
- method public void removeOnSubscriptionsChangedListener(@NonNull android.telephony.SubscriptionManager.OnSubscriptionsChangedListener);
- }
-
public final class UiccAccessRule implements android.os.Parcelable {
ctor public UiccAccessRule(byte[], @Nullable String, long);
method public int describeContents();
diff --git a/api/test-current.txt b/api/test-current.txt
index b3e80a9..c2553ed 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -14,6 +14,7 @@
field public static final String FORCE_STOP_PACKAGES = "android.permission.FORCE_STOP_PACKAGES";
field public static final String MANAGE_ACTIVITY_STACKS = "android.permission.MANAGE_ACTIVITY_STACKS";
field public static final String MANAGE_ROLLBACKS = "android.permission.MANAGE_ROLLBACKS";
+ field public static final String NETWORK_SETTINGS = "android.permission.NETWORK_SETTINGS";
field public static final String NETWORK_STACK = "android.permission.NETWORK_STACK";
field public static final String READ_CELL_BROADCASTS = "android.permission.READ_CELL_BROADCASTS";
field public static final String REMOVE_TASKS = "android.permission.REMOVE_TASKS";
@@ -1439,7 +1440,7 @@
}
public class EthernetManager {
- method @NonNull public android.net.EthernetManager.TetheredInterfaceRequest requestTetheredInterface(@NonNull android.net.EthernetManager.TetheredInterfaceCallback);
+ method @NonNull public android.net.EthernetManager.TetheredInterfaceRequest requestTetheredInterface(@NonNull java.util.concurrent.Executor, @NonNull android.net.EthernetManager.TetheredInterfaceCallback);
}
public static interface EthernetManager.TetheredInterfaceCallback {
diff --git a/cmds/idmap2/idmap2d/Idmap2Service.cpp b/cmds/idmap2/idmap2d/Idmap2Service.cpp
index 8ee79f6..d723776 100644
--- a/cmds/idmap2/idmap2d/Idmap2Service.cpp
+++ b/cmds/idmap2/idmap2d/Idmap2Service.cpp
@@ -113,10 +113,10 @@
Status Idmap2Service::createIdmap(const std::string& target_apk_path,
const std::string& overlay_apk_path, int32_t fulfilled_policies,
bool enforce_overlayable, int32_t user_id ATTRIBUTE_UNUSED,
- std::unique_ptr<std::string>* _aidl_return) {
+ std::optional<std::string>* _aidl_return) {
assert(_aidl_return);
SYSTRACE << "Idmap2Service::createIdmap " << target_apk_path << " " << overlay_apk_path;
- _aidl_return->reset(nullptr);
+ _aidl_return->reset();
const PolicyBitmask policy_bitmask = ConvertAidlArgToPolicyBitmask(fulfilled_policies);
@@ -155,7 +155,7 @@
return error("failed to write to idmap path " + idmap_path);
}
- *_aidl_return = std::make_unique<std::string>(idmap_path);
+ *_aidl_return = idmap_path;
return ok();
}
diff --git a/cmds/idmap2/idmap2d/Idmap2Service.h b/cmds/idmap2/idmap2d/Idmap2Service.h
index 1aab059..73a236a 100644
--- a/cmds/idmap2/idmap2d/Idmap2Service.h
+++ b/cmds/idmap2/idmap2d/Idmap2Service.h
@@ -20,7 +20,7 @@
#include <android-base/unique_fd.h>
#include <binder/BinderService.h>
-#include <memory>
+#include <optional>
#include <string>
#include "android/os/BnIdmap2.h"
@@ -45,7 +45,7 @@
binder::Status createIdmap(const std::string& target_apk_path,
const std::string& overlay_apk_path, int32_t fulfilled_policies,
bool enforce_overlayable, int32_t user_id,
- std::unique_ptr<std::string>* _aidl_return);
+ std::optional<std::string>* _aidl_return);
};
} // namespace android::os
diff --git a/cmds/statsd/src/StatsLogProcessor.cpp b/cmds/statsd/src/StatsLogProcessor.cpp
index c9e026b..af4f675 100644
--- a/cmds/statsd/src/StatsLogProcessor.cpp
+++ b/cmds/statsd/src/StatsLogProcessor.cpp
@@ -320,11 +320,6 @@
mAnomalyAlarmMonitor, mPeriodicAlarmMonitor);
if (newMetricsManager->isConfigValid()) {
mUidMap->OnConfigUpdated(key);
- if (newMetricsManager->shouldAddUidMapListener()) {
- // We have to add listener after the MetricsManager is constructed because it's
- // not safe to create wp or sp from this pointer inside its constructor.
- mUidMap->addListener(newMetricsManager.get());
- }
newMetricsManager->refreshTtl(timestampNs);
mMetricsManagers[key] = newMetricsManager;
VLOG("StatsdConfig valid");
@@ -743,6 +738,32 @@
}
}
+void StatsLogProcessor::notifyAppUpgrade(const int64_t& eventTimeNs, const string& apk,
+ const int uid, const int64_t version) {
+ std::lock_guard<std::mutex> lock(mMetricsMutex);
+ ALOGW("Received app upgrade");
+ for (auto it : mMetricsManagers) {
+ it.second->notifyAppUpgrade(eventTimeNs, apk, uid, version);
+ }
+}
+
+void StatsLogProcessor::notifyAppRemoved(const int64_t& eventTimeNs, const string& apk,
+ const int uid) {
+ std::lock_guard<std::mutex> lock(mMetricsMutex);
+ ALOGW("Received app removed");
+ for (auto it : mMetricsManagers) {
+ it.second->notifyAppRemoved(eventTimeNs, apk, uid);
+ }
+}
+
+void StatsLogProcessor::onUidMapReceived(const int64_t& eventTimeNs) {
+ std::lock_guard<std::mutex> lock(mMetricsMutex);
+ ALOGW("Received uid map");
+ for (auto it : mMetricsManagers) {
+ it.second->onUidMapReceived(eventTimeNs);
+ }
+}
+
void StatsLogProcessor::noteOnDiskData(const ConfigKey& key) {
std::lock_guard<std::mutex> lock(mMetricsMutex);
mOnDiskDataConfigs.insert(key);
diff --git a/cmds/statsd/src/StatsLogProcessor.h b/cmds/statsd/src/StatsLogProcessor.h
index 313e16d..0d2b33e 100644
--- a/cmds/statsd/src/StatsLogProcessor.h
+++ b/cmds/statsd/src/StatsLogProcessor.h
@@ -32,7 +32,7 @@
namespace statsd {
-class StatsLogProcessor : public ConfigListener {
+class StatsLogProcessor : public ConfigListener, public virtual PackageInfoListener {
public:
StatsLogProcessor(const sp<UidMap>& uidMap, const sp<StatsPullerManager>& pullerManager,
const sp<AlarmMonitor>& anomalyAlarmMonitor,
@@ -91,6 +91,16 @@
/* Sets the active status/ttl for all configs and metrics to the status in ActiveConfigList. */
void SetConfigsActiveState(const ActiveConfigList& activeConfigList, int64_t currentTimeNs);
+ /* Notify all MetricsManagers of app upgrades */
+ void notifyAppUpgrade(const int64_t& eventTimeNs, const string& apk, const int uid,
+ const int64_t version) override;
+
+ /* Notify all MetricsManagers of app removals */
+ void notifyAppRemoved(const int64_t& eventTimeNs, const string& apk, const int uid) override;
+
+ /* Notify all MetricsManagers of uid map snapshots received */
+ void onUidMapReceived(const int64_t& eventTimeNs) override;
+
// Reset all configs.
void resetConfigs();
diff --git a/cmds/statsd/src/StatsService.cpp b/cmds/statsd/src/StatsService.cpp
index d6c1380..dd65155 100644
--- a/cmds/statsd/src/StatsService.cpp
+++ b/cmds/statsd/src/StatsService.cpp
@@ -200,6 +200,7 @@
}
});
+ mUidMap->setListener(mProcessor);
mConfigManager->AddListener(mProcessor);
init_system_properties();
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index 6cd34ae..7a63493 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -355,7 +355,7 @@
BootTimeEventElapsedTime boot_time_event_elapsed_time_reported = 240;
BootTimeEventUtcTime boot_time_event_utc_time_reported = 241;
BootTimeEventErrorCode boot_time_event_error_code_reported = 242;
- UserspaceRebootReported userspace_reboot_reported = 243;
+ UserspaceRebootReported userspace_reboot_reported = 243 [(log_from_module) = "framework"];
}
// Pulled events will start at field 10000.
diff --git a/cmds/statsd/src/metrics/MetricProducer.h b/cmds/statsd/src/metrics/MetricProducer.h
index 09ad290..a0c8224 100644
--- a/cmds/statsd/src/metrics/MetricProducer.h
+++ b/cmds/statsd/src/metrics/MetricProducer.h
@@ -73,7 +73,7 @@
// writing the report to dropbox. MetricProducers should respond to package changes as required in
// PackageInfoListener, but if none of the metrics are slicing by package name, then the update can
// be a no-op.
-class MetricProducer : public virtual PackageInfoListener {
+class MetricProducer : public virtual android::RefBase {
public:
MetricProducer(const int64_t& metricId, const ConfigKey& key, const int64_t timeBaseNs,
const int conditionIndex, const sp<ConditionWizard>& wizard)
@@ -105,8 +105,8 @@
* the flush again when the end timestamp is forced to be now, and then after flushing, update
* the start timestamp to be now.
*/
- void notifyAppUpgrade(const int64_t& eventTimeNs, const string& apk, const int uid,
- const int64_t version) override {
+ virtual void notifyAppUpgrade(const int64_t& eventTimeNs, const string& apk, const int uid,
+ const int64_t version) {
std::lock_guard<std::mutex> lock(mMutex);
if (eventTimeNs > getCurrentBucketEndTimeNs()) {
@@ -119,16 +119,11 @@
// is a partial bucket and can merge it with the previous bucket.
};
- void notifyAppRemoved(const int64_t& eventTimeNs, const string& apk, const int uid) override{
+ void notifyAppRemoved(const int64_t& eventTimeNs, const string& apk, const int uid) {
// Force buckets to split on removal also.
notifyAppUpgrade(eventTimeNs, apk, uid, 0);
};
- void onUidMapReceived(const int64_t& eventTimeNs) override{
- // Purposefully don't flush partial buckets on a new snapshot.
- // This occurs if a new user is added/removed or statsd crashes.
- };
-
// Consume the parsed stats log entry that already matched the "what" of the metric.
void onMatchedLogEvent(const size_t matcherIndex, const LogEvent& event) {
std::lock_guard<std::mutex> lock(mMutex);
diff --git a/cmds/statsd/src/metrics/MetricsManager.cpp b/cmds/statsd/src/metrics/MetricsManager.cpp
index 7b7d0ca..760e800 100644
--- a/cmds/statsd/src/metrics/MetricsManager.cpp
+++ b/cmds/statsd/src/metrics/MetricsManager.cpp
@@ -175,6 +175,10 @@
void MetricsManager::notifyAppUpgrade(const int64_t& eventTimeNs, const string& apk, const int uid,
const int64_t version) {
+ // Inform all metric producers.
+ for (auto it : mAllMetricProducers) {
+ it->notifyAppUpgrade(eventTimeNs, apk, uid, version);
+ }
// check if we care this package
if (std::find(mAllowedPkg.begin(), mAllowedPkg.end(), apk) == mAllowedPkg.end()) {
return;
@@ -186,6 +190,10 @@
void MetricsManager::notifyAppRemoved(const int64_t& eventTimeNs, const string& apk,
const int uid) {
+ // Inform all metric producers.
+ for (auto it : mAllMetricProducers) {
+ it->notifyAppRemoved(eventTimeNs, apk, uid);
+ }
// check if we care this package
if (std::find(mAllowedPkg.begin(), mAllowedPkg.end(), apk) == mAllowedPkg.end()) {
return;
@@ -196,6 +204,9 @@
}
void MetricsManager::onUidMapReceived(const int64_t& eventTimeNs) {
+ // Purposefully don't inform metric producers on a new snapshot
+ // because we don't need to flush partial buckets.
+ // This occurs if a new user is added/removed or statsd crashes.
if (mAllowedPkg.size() == 0) {
return;
}
diff --git a/cmds/statsd/src/metrics/MetricsManager.h b/cmds/statsd/src/metrics/MetricsManager.h
index 8efca1e..34d47d4 100644
--- a/cmds/statsd/src/metrics/MetricsManager.h
+++ b/cmds/statsd/src/metrics/MetricsManager.h
@@ -35,7 +35,7 @@
namespace statsd {
// A MetricsManager is responsible for managing metrics from one single config source.
-class MetricsManager : public PackageInfoListener {
+class MetricsManager : public virtual android::RefBase {
public:
MetricsManager(const ConfigKey& configKey, const StatsdConfig& config, const int64_t timeBaseNs,
const int64_t currentTimeNs, const sp<UidMap>& uidMap,
@@ -63,15 +63,11 @@
unordered_set<sp<const InternalAlarm>, SpHash<InternalAlarm>>& alarmSet);
void notifyAppUpgrade(const int64_t& eventTimeNs, const string& apk, const int uid,
- const int64_t version) override;
+ const int64_t version);
- void notifyAppRemoved(const int64_t& eventTimeNs, const string& apk, const int uid) override;
+ void notifyAppRemoved(const int64_t& eventTimeNs, const string& apk, const int uid);
- void onUidMapReceived(const int64_t& eventTimeNs) override;
-
- bool shouldAddUidMapListener() const {
- return !mAllowedPkg.empty();
- }
+ void onUidMapReceived(const int64_t& eventTimeNs);
bool shouldWriteToDisk() const {
return mNoReportMetricIds.size() != mAllMetricProducers.size();
diff --git a/cmds/statsd/src/metrics/metrics_manager_util.cpp b/cmds/statsd/src/metrics/metrics_manager_util.cpp
index 46442b5..f5f2479 100644
--- a/cmds/statsd/src/metrics/metrics_manager_util.cpp
+++ b/cmds/statsd/src/metrics/metrics_manager_util.cpp
@@ -286,7 +286,7 @@
}
bool initMetrics(const ConfigKey& key, const StatsdConfig& config, const int64_t timeBaseTimeNs,
- const int64_t currentTimeNs, UidMap& uidMap,
+ const int64_t currentTimeNs,
const sp<StatsPullerManager>& pullerManager,
const unordered_map<int64_t, int>& logTrackerMap,
const unordered_map<int64_t, int>& conditionTrackerMap,
@@ -600,9 +600,6 @@
}
noReportMetricIds.insert(no_report_metric);
}
- for (const auto& it : allMetricProducers) {
- uidMap.addListener(it);
- }
return true;
}
@@ -807,7 +804,7 @@
return false;
}
- if (!initMetrics(key, config, timeBaseNs, currentTimeNs, uidMap, pullerManager, logTrackerMap,
+ if (!initMetrics(key, config, timeBaseNs, currentTimeNs, pullerManager, logTrackerMap,
conditionTrackerMap, allAtomMatchers, allConditionTrackers, allMetricProducers,
conditionToMetricMap, trackerToMetricMap, metricProducerMap,
noReportMetricIds)) {
diff --git a/cmds/statsd/src/packages/UidMap.cpp b/cmds/statsd/src/packages/UidMap.cpp
index d4b57dd..7e63bbf 100644
--- a/cmds/statsd/src/packages/UidMap.cpp
+++ b/cmds/statsd/src/packages/UidMap.cpp
@@ -119,7 +119,7 @@
void UidMap::updateMap(const int64_t& timestamp, const vector<int32_t>& uid,
const vector<int64_t>& versionCode, const vector<String16>& versionString,
const vector<String16>& packageName, const vector<String16>& installer) {
- vector<wp<PackageInfoListener>> broadcastList;
+ wp<PackageInfoListener> broadcast = NULL;
{
lock_guard<mutex> lock(mMutex); // Exclusively lock for updates.
@@ -150,25 +150,22 @@
ensureBytesUsedBelowLimit();
StatsdStats::getInstance().setCurrentUidMapMemory(mBytesUsed);
- getListenerListCopyLocked(&broadcastList);
+ broadcast = mSubscriber;
}
// To avoid invoking callback while holding the internal lock. we get a copy of the listener
- // list and invoke the callback. It's still possible that after we copy the list, a
- // listener removes itself before we call it. It's then the listener's job to handle it (expect
- // the callback to be called after listener is removed, and the listener should properly
- // ignore it).
- for (const auto& weakPtr : broadcastList) {
- auto strongPtr = weakPtr.promote();
- if (strongPtr != NULL) {
- strongPtr->onUidMapReceived(timestamp);
- }
+ // and invoke the callback. It's still possible that after we copy the listener, it removes
+ // itself before we call it. It's then the listener's job to handle it (expect the callback to
+ // be called after listener is removed, and the listener should properly ignore it).
+ auto strongPtr = broadcast.promote();
+ if (strongPtr != NULL) {
+ strongPtr->onUidMapReceived(timestamp);
}
}
void UidMap::updateApp(const int64_t& timestamp, const String16& app_16, const int32_t& uid,
const int64_t& versionCode, const String16& versionString,
const String16& installer) {
- vector<wp<PackageInfoListener>> broadcastList;
+ wp<PackageInfoListener> broadcast = NULL;
string appName = string(String8(app_16).string());
{
lock_guard<mutex> lock(mMutex);
@@ -195,7 +192,7 @@
// for the first time, then we don't notify the listeners.
// It's also OK to split again if we're forming a partial bucket after re-installing an
// app after deletion.
- getListenerListCopyLocked(&broadcastList);
+ broadcast = mSubscriber;
}
mChanges.emplace_back(false, timestamp, appName, uid, versionCode, newVersionString,
prevVersion, prevVersionString);
@@ -205,11 +202,9 @@
StatsdStats::getInstance().setUidMapChanges(mChanges.size());
}
- for (const auto& weakPtr : broadcastList) {
- auto strongPtr = weakPtr.promote();
- if (strongPtr != NULL) {
- strongPtr->notifyAppUpgrade(timestamp, appName, uid, versionCode);
- }
+ auto strongPtr = broadcast.promote();
+ if (strongPtr != NULL) {
+ strongPtr->notifyAppUpgrade(timestamp, appName, uid, versionCode);
}
}
@@ -230,21 +225,8 @@
}
}
-void UidMap::getListenerListCopyLocked(vector<wp<PackageInfoListener>>* output) {
- for (auto weakIt = mSubscribers.begin(); weakIt != mSubscribers.end();) {
- auto strongPtr = weakIt->promote();
- if (strongPtr != NULL) {
- output->push_back(*weakIt);
- weakIt++;
- } else {
- weakIt = mSubscribers.erase(weakIt);
- VLOG("The UidMap listener is gone, remove it now");
- }
- }
-}
-
void UidMap::removeApp(const int64_t& timestamp, const String16& app_16, const int32_t& uid) {
- vector<wp<PackageInfoListener>> broadcastList;
+ wp<PackageInfoListener> broadcast = NULL;
string app = string(String8(app_16).string());
{
lock_guard<mutex> lock(mMutex);
@@ -271,25 +253,18 @@
ensureBytesUsedBelowLimit();
StatsdStats::getInstance().setCurrentUidMapMemory(mBytesUsed);
StatsdStats::getInstance().setUidMapChanges(mChanges.size());
- getListenerListCopyLocked(&broadcastList);
+ broadcast = mSubscriber;
}
- for (const auto& weakPtr : broadcastList) {
- auto strongPtr = weakPtr.promote();
- if (strongPtr != NULL) {
- strongPtr->notifyAppRemoved(timestamp, app, uid);
- }
+ auto strongPtr = broadcast.promote();
+ if (strongPtr != NULL) {
+ strongPtr->notifyAppRemoved(timestamp, app, uid);
}
}
-void UidMap::addListener(wp<PackageInfoListener> producer) {
+void UidMap::setListener(wp<PackageInfoListener> listener) {
lock_guard<mutex> lock(mMutex); // Lock for updates
- mSubscribers.insert(producer);
-}
-
-void UidMap::removeListener(wp<PackageInfoListener> producer) {
- lock_guard<mutex> lock(mMutex); // Lock for updates
- mSubscribers.erase(producer);
+ mSubscriber = listener;
}
void UidMap::assignIsolatedUid(int isolatedUid, int parentUid) {
diff --git a/cmds/statsd/src/packages/UidMap.h b/cmds/statsd/src/packages/UidMap.h
index a7c5fb2..2d3f6ee 100644
--- a/cmds/statsd/src/packages/UidMap.h
+++ b/cmds/statsd/src/packages/UidMap.h
@@ -118,12 +118,10 @@
// adb shell cmd stats print-uid-map
void printUidMap(int outFd) const;
- // Commands for indicating to the map that a producer should be notified if an app is updated.
- // This allows the metric producer to distinguish when the same uid or app represents a
- // different version of an app.
- void addListener(wp<PackageInfoListener> producer);
- // Remove the listener from the set of metric producers that subscribe to updates.
- void removeListener(wp<PackageInfoListener> producer);
+ // Command for indicating to the map that StatsLogProcessor should be notified if an app is
+ // updated. This allows metric producers and managers to distinguish when the same uid or app
+ // represents a different version of an app.
+ void setListener(wp<PackageInfoListener> listener);
// Informs uid map that a config is added/updated. Used for keeping mConfigKeys up to date.
void OnConfigUpdated(const ConfigKey& key);
@@ -167,8 +165,6 @@
std::set<string> getAppNamesFromUidLocked(const int32_t& uid, bool returnNormalized) const;
string normalizeAppName(const string& appName) const;
- void getListenerListCopyLocked(std::vector<wp<PackageInfoListener>>* output);
-
void writeUidMapSnapshotLocked(int64_t timestamp, bool includeVersionStrings,
bool includeInstaller, const std::set<int32_t>& interestingUids,
std::set<string>* str_set, ProtoOutputStream* proto);
@@ -195,8 +191,8 @@
// Store which uid and apps represent deleted ones.
std::list<std::pair<int, string>> mDeletedApps;
- // Metric producers that should be notified if there's an upgrade in any app.
- set<wp<PackageInfoListener>> mSubscribers;
+ // Notify StatsLogProcessor if there's an upgrade/removal in any app.
+ wp<PackageInfoListener> mSubscriber;
// Mapping of config keys we're aware of to the epoch time they last received an update. This
// lets us know it's safe to delete events older than the oldest update. The value is nanosec.
diff --git a/cmds/statsd/tests/e2e/PartialBucket_e2e_test.cpp b/cmds/statsd/tests/e2e/PartialBucket_e2e_test.cpp
index 309d251..0bc3ebb 100644
--- a/cmds/statsd/tests/e2e/PartialBucket_e2e_test.cpp
+++ b/cmds/statsd/tests/e2e/PartialBucket_e2e_test.cpp
@@ -162,7 +162,10 @@
ConfigMetricsReport report = GetReports(service.mProcessor, start + 4);
backfillStartEndTimestamp(&report);
- EXPECT_EQ(1, report.metrics_size());
+
+ ASSERT_EQ(1, report.metrics_size());
+ ASSERT_EQ(1, report.metrics(0).count_metrics().data_size());
+ ASSERT_EQ(1, report.metrics(0).count_metrics().data(0).bucket_info_size());
EXPECT_TRUE(report.metrics(0).count_metrics().data(0).bucket_info(0).
has_start_bucket_elapsed_nanos());
EXPECT_TRUE(report.metrics(0).count_metrics().data(0).bucket_info(0).
@@ -186,7 +189,10 @@
ConfigMetricsReport report = GetReports(service.mProcessor, start + 4);
backfillStartEndTimestamp(&report);
- EXPECT_EQ(1, report.metrics_size());
+
+ ASSERT_EQ(1, report.metrics_size());
+ ASSERT_EQ(1, report.metrics(0).count_metrics().data_size());
+ ASSERT_EQ(1, report.metrics(0).count_metrics().data(0).bucket_info_size());
EXPECT_TRUE(report.metrics(0).count_metrics().data(0).bucket_info(0).
has_start_bucket_elapsed_nanos());
EXPECT_TRUE(report.metrics(0).count_metrics().data(0).bucket_info(0).
@@ -228,8 +234,9 @@
ConfigMetricsReport report =
GetReports(service.mProcessor, 5 * 60 * NS_PER_SEC + start + 100 * NS_PER_SEC, true);
backfillStartEndTimestamp(&report);
- EXPECT_EQ(1, report.metrics_size());
- EXPECT_EQ(1, report.metrics(0).value_metrics().skipped_size());
+
+ ASSERT_EQ(1, report.metrics_size());
+ ASSERT_EQ(1, report.metrics(0).value_metrics().skipped_size());
EXPECT_TRUE(report.metrics(0).value_metrics().skipped(0).has_start_bucket_elapsed_nanos());
// Can't test the start time since it will be based on the actual time when the pulling occurs.
EXPECT_EQ(MillisToNano(NanoToMillis(endSkipped)),
@@ -270,8 +277,8 @@
ConfigMetricsReport report =
GetReports(service.mProcessor, 5 * 60 * NS_PER_SEC + start + 100 * NS_PER_SEC, true);
backfillStartEndTimestamp(&report);
- EXPECT_EQ(1, report.metrics_size());
- EXPECT_EQ(1, report.metrics(0).gauge_metrics().skipped_size());
+ ASSERT_EQ(1, report.metrics_size());
+ ASSERT_EQ(1, report.metrics(0).gauge_metrics().skipped_size());
// Can't test the start time since it will be based on the actual time when the pulling occurs.
EXPECT_TRUE(report.metrics(0).gauge_metrics().skipped(0).has_start_bucket_elapsed_nanos());
EXPECT_EQ(MillisToNano(NanoToMillis(endSkipped)),
diff --git a/core/java/android/accessibilityservice/OWNERS b/core/java/android/accessibilityservice/OWNERS
index 265674a..c6f42f7 100644
--- a/core/java/android/accessibilityservice/OWNERS
+++ b/core/java/android/accessibilityservice/OWNERS
@@ -1,3 +1,4 @@
svetoslavganov@google.com
pweaver@google.com
rhedjao@google.com
+qasid@google.com
diff --git a/core/java/android/app/DexLoadReporter.java b/core/java/android/app/DexLoadReporter.java
index 229bee5..5bc9992 100644
--- a/core/java/android/app/DexLoadReporter.java
+++ b/core/java/android/app/DexLoadReporter.java
@@ -28,9 +28,8 @@
import java.io.File;
import java.io.IOException;
-import java.util.ArrayList;
import java.util.HashSet;
-import java.util.List;
+import java.util.Map;
import java.util.Set;
/**
@@ -87,50 +86,32 @@
}
@Override
- public void report(List<ClassLoader> classLoadersChain, List<String> classPaths) {
- if (classLoadersChain.size() != classPaths.size()) {
- Slog.wtf(TAG, "Bad call to DexLoadReporter: argument size mismatch");
- return;
- }
- if (classPaths.isEmpty()) {
- Slog.wtf(TAG, "Bad call to DexLoadReporter: empty dex paths");
- return;
- }
-
- // The first element of classPaths is the list of dex files that should be registered.
- // The classpath is represented as a list of dex files separated by File.pathSeparator.
- String[] dexPathsForRegistration = classPaths.get(0).split(File.pathSeparator);
- if (dexPathsForRegistration.length == 0) {
- // No dex files to register.
+ public void report(Map<String, String> classLoaderContextMap) {
+ if (classLoaderContextMap.isEmpty()) {
+ Slog.wtf(TAG, "Bad call to DexLoadReporter: empty classLoaderContextMap");
return;
}
// Notify the package manager about the dex loads unconditionally.
// The load might be for either a primary or secondary dex file.
- notifyPackageManager(classLoadersChain, classPaths);
+ notifyPackageManager(classLoaderContextMap);
// Check for secondary dex files and register them for profiling if possible.
// Note that we only register the dex paths belonging to the first class loader.
- registerSecondaryDexForProfiling(dexPathsForRegistration);
+ registerSecondaryDexForProfiling(classLoaderContextMap.keySet());
}
- private void notifyPackageManager(List<ClassLoader> classLoadersChain,
- List<String> classPaths) {
+ private void notifyPackageManager(Map<String, String> classLoaderContextMap) {
// Get the class loader names for the binder call.
- List<String> classLoadersNames = new ArrayList<>(classPaths.size());
- for (ClassLoader classLoader : classLoadersChain) {
- classLoadersNames.add(classLoader.getClass().getName());
- }
String packageName = ActivityThread.currentPackageName();
try {
- ActivityThread.getPackageManager().notifyDexLoad(
- packageName, classLoadersNames, classPaths,
- VMRuntime.getRuntime().vmInstructionSet());
+ ActivityThread.getPackageManager().notifyDexLoad(packageName,
+ classLoaderContextMap, VMRuntime.getRuntime().vmInstructionSet());
} catch (RemoteException re) {
Slog.e(TAG, "Failed to notify PM about dex load for package " + packageName, re);
}
}
- private void registerSecondaryDexForProfiling(String[] dexPaths) {
+ private void registerSecondaryDexForProfiling(Set<String> dexPaths) {
if (!SystemProperties.getBoolean("dalvik.vm.dexopt.secondary", false)) {
return;
}
diff --git a/core/java/android/app/PropertyInvalidatedCache.java b/core/java/android/app/PropertyInvalidatedCache.java
new file mode 100644
index 0000000..844e72e
--- /dev/null
+++ b/core/java/android/app/PropertyInvalidatedCache.java
@@ -0,0 +1,428 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app;
+import android.annotation.NonNull;
+import android.os.SystemProperties;
+import android.util.Log;
+
+import com.android.internal.annotations.GuardedBy;
+
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.Random;
+import java.util.concurrent.atomic.AtomicLong;
+
+/**
+ * LRU cache that's invalidated when an opaque value in a property changes. Self-synchronizing,
+ * but doesn't hold a lock across data fetches on query misses.
+ *
+ * The intended use case is caching frequently-read, seldom-changed information normally
+ * retrieved across interprocess communication. Imagine that you've written a user birthday
+ * information daemon called "birthdayd" that exposes an {@code IUserBirthdayService} interface
+ * over binder. That binder interface looks something like this:
+ *
+ * <pre>
+ * parcelable Birthday {
+ * int month;
+ * int day;
+ * }
+ * interface IUserBirthdayService {
+ * Birthday getUserBirthday(int userId);
+ * }
+ * </pre>
+ *
+ * Suppose the service implementation itself looks like this...
+ *
+ * <pre>
+ * public class UserBirthdayServiceImpl implements IUserBirthdayService {
+ * private final HashMap<Integer, Birthday> mUidToBirthday;
+ * @Override
+ * public synchronized Birthday getUserBirthday(int userId) {
+ * return mUidToBirthday.get(userId);
+ * }
+ * private synchronized void updateBirthdays(Map<Integer, Birthday> uidToBirthday) {
+ * mUidToBirthday.clear();
+ * mUidToBirthday.putAll(uidToBirthday);
+ * }
+ * }
+ * </pre>
+ *
+ * ... and we have a client in frameworks (loaded into every app process) that looks
+ * like this:
+ *
+ * <pre>
+ * public class ActivityThread {
+ * ...
+ * public Birthday getUserBirthday(int userId) {
+ * return GetService("birthdayd").getUserBirthday(userId);
+ * }
+ * ...
+ * }
+ * </pre>
+ *
+ * With this code, every time an app calls {@code getUserBirthday(uid)}, we make a binder call
+ * to the birthdayd process and consult its database of birthdays. If we query user birthdays
+ * frequently, we do a lot of work that we don't have to do, since user birthdays
+ * change infrequently.
+ *
+ * PropertyInvalidatedCache is part of a pattern for optimizing this kind of
+ * information-querying code. Using {@code PropertyInvalidatedCache}, you'd write the client
+ * this way:
+ *
+ * <pre>
+ * public class ActivityThread {
+ * ...
+ * private static final int BDAY_CACHE_MAX = 8; // Maximum birthdays to cache
+ * private static final String BDAY_CACHE_KEY = "cache_key.birthdayd";
+ * private final PropertyInvalidatedCache<Integer, Birthday> mBirthdayCache = new
+ * PropertyInvalidatedCache<Integer, Birthday>(BDAY_CACHE_MAX, BDAY_CACHE_KEY) {
+ * @Override
+ * protected Birthday recompute(Integer userId) {
+ * return GetService("birthdayd").getUserBirthday(userId);
+ * }
+ * };
+ * public void disableUserBirthdayCache() {
+ * mBirthdayCache.disableLocal();
+ * }
+ * public void invalidateUserBirthdayCache() {
+ * mBirthdayCache.invalidateCache();
+ * }
+ * public Birthday getUserBirthday(int userId) {
+ * return mBirthdayCache.query(userId);
+ * }
+ * ...
+ * }
+ * </pre>
+ *
+ * With this cache, clients perform a binder call to birthdayd if asking for a user's birthday
+ * for the first time; on subsequent queries, we return the already-known Birthday object.
+ *
+ * User birthdays do occasionally change, so we have to modify the server to invalidate this
+ * cache when necessary. That invalidation code looks like this:
+ *
+ * <pre>
+ * public class UserBirthdayServiceImpl {
+ * ...
+ * public UserBirthdayServiceImpl() {
+ * ...
+ * ActivityThread.currentActivityThread().disableUserBirthdayCache();
+ * ActivityThread.currentActivityThread().invalidateUserBirthdayCache();
+ * }
+ *
+ * private synchronized void updateBirthdays(Map<Integer, Birthday> uidToBirthday) {
+ * mUidToBirthday.clear();
+ * mUidToBirthday.putAll(uidToBirthday);
+ * ActivityThread.currentActivityThread().invalidateUserBirthdayCache();
+ * }
+ * ...
+ * }
+ * </pre>
+ *
+ * The call to {@code PropertyInvalidatedCache.invalidateCache()} guarantees that all clients
+ * will re-fetch birthdays from binder during consequent calls to
+ * {@code ActivityThread.getUserBirthday()}. Because the invalidate call happens with the lock
+ * held, we maintain consistency between different client views of the birthday state. The use
+ * of PropertyInvalidatedCache in this idiomatic way introduces no new race conditions.
+ *
+ * PropertyInvalidatedCache has a few other features for doing things like incremental
+ * enhancement of cached values and invalidation of multiple caches (that all share the same
+ * property key) at once.
+ *
+ * {@code BDAY_CACHE_KEY} is the name of a property that we set to an opaque unique value each
+ * time we update the cache. SELinux configuration must allow everyone to read this property
+ * and it must allow any process that needs to invalidate the cache (here, birthdayd) to write
+ * the property. (These properties conventionally begin with the "cache_key." prefix.)
+ *
+ * The {@code UserBirthdayServiceImpl} constructor calls {@code disableUserBirthdayCache()} so
+ * that calls to {@code getUserBirthday} from inside birthdayd don't go through the cache. In
+ * this local case, there's no IPC, so use of the cache is (depending on exact
+ * circumstance) unnecessary.
+ *
+ * @param <Query> The class used to index cache entries: must be hashable and comparable
+ * @param <Result> The class holding cache entries; use a boxed primitive if possible
+ *
+ * {@hide}
+ */
+public abstract class PropertyInvalidatedCache<Query, Result> {
+ private static final long NONCE_UNSET = 0;
+ private static final long NONCE_DISABLED = -1;
+
+ private static final String TAG = "PropertyInvalidatedCache";
+ private static final boolean DEBUG = false;
+ private static final boolean ENABLE = true;
+
+ private final Object mLock = new Object();
+
+ /**
+ * Name of the property that holds the unique value that we use to invalidate the cache.
+ */
+ private final String mPropertyName;
+
+ /**
+ * Handle to the {@code mPropertyName} property, transitioning to non-{@code null} once the
+ * property exists on the system.
+ */
+ private volatile SystemProperties.Handle mPropertyHandle;
+
+ @GuardedBy("mLock")
+ private final LinkedHashMap<Query, Result> mCache;
+
+ /**
+ * The last value of the {@code mPropertyHandle} that we observed.
+ */
+ @GuardedBy("mLock")
+ private long mLastSeenNonce = NONCE_UNSET;
+
+ /**
+ * Whether we've disabled the cache in this process.
+ */
+ private boolean mDisabled = false;
+
+ /**
+ * Make a new property invalidated cache.
+ *
+ * @param maxEntries Maximum number of entries to cache; LRU discard
+ * @param propertyName Name of the system property holding the cache invalidation nonce
+ */
+ public PropertyInvalidatedCache(int maxEntries, @NonNull String propertyName) {
+ mPropertyName = propertyName;
+ mCache = new LinkedHashMap<Query, Result>(
+ 2 /* start small */,
+ 0.75f /* default load factor */,
+ true /* LRU access order */) {
+ @Override
+ protected boolean removeEldestEntry(Map.Entry eldest) {
+ return size() > maxEntries;
+ }
+ };
+ }
+
+ /**
+ * Forget all cached values.
+ */
+ public final void clear() {
+ synchronized (mLock) {
+ mCache.clear();
+ }
+ }
+
+ /**
+ * Fetch a result from scratch in case it's not in the cache at all. Called unlocked: may
+ * block. If this function returns null, the result of the cache query is null. There is no
+ * "negative cache" in the query: we don't cache null results at all.
+ */
+ protected abstract Result recompute(Query query);
+
+ /**
+ * Make result up-to-date on a cache hit. Called unlocked;
+ * may block.
+ *
+ * Return either 1) oldResult itself (the same object, by reference equality), in which
+ * case we just return oldResult as the result of the cache query, 2) a new object, which
+ * replaces oldResult in the cache and which we return as the result of the cache query
+ * after performing another property read to make sure that the result hasn't changed in
+ * the meantime (if the nonce has changed in the meantime, we drop the cache and try the
+ * whole query again), or 3) null, which causes the old value to be removed from the cache
+ * and null to be returned as the result of the cache query.
+ */
+ protected Result refresh(Result oldResult, Query query) {
+ return oldResult;
+ }
+
+ private long getCurrentNonce() {
+ SystemProperties.Handle handle = mPropertyHandle;
+ if (handle == null) {
+ handle = SystemProperties.find(mPropertyName);
+ if (handle == null) {
+ return NONCE_UNSET;
+ }
+ mPropertyHandle = handle;
+ }
+ return handle.getLong(NONCE_UNSET);
+ }
+
+ /**
+ * Disable the use of this cache in this process.
+ */
+ public final void disableLocal() {
+ synchronized (mLock) {
+ mDisabled = true;
+ mCache.clear();
+ }
+ }
+
+ /**
+ * Return whether the cache is disabled in this process.
+ */
+ public final boolean isDisabledLocal() {
+ return mDisabled;
+ }
+
+ /**
+ * Get a value from the cache or recompute it.
+ */
+ public Result query(Query query) {
+ // Let access to mDisabled race: it's atomic anyway.
+ long currentNonce = (ENABLE && !mDisabled) ? getCurrentNonce() : NONCE_DISABLED;
+ for (;;) {
+ if (currentNonce == NONCE_DISABLED || currentNonce == NONCE_UNSET) {
+ if (DEBUG) {
+ Log.d(TAG,
+ String.format("cache %s for %s",
+ currentNonce == NONCE_DISABLED ? "disabled" : "unset",
+ query));
+ }
+ return recompute(query);
+ }
+ final Result cachedResult;
+ synchronized (mLock) {
+ if (currentNonce == mLastSeenNonce) {
+ cachedResult = mCache.get(query);
+ } else {
+ if (DEBUG) {
+ Log.d(TAG,
+ String.format("clearing cache because nonce changed [%s] -> [%s]",
+ mLastSeenNonce, currentNonce));
+ }
+ mCache.clear();
+ mLastSeenNonce = currentNonce;
+ cachedResult = null;
+ }
+ }
+ // Cache hit --- but we're not quite done yet. A value in the cache might need to
+ // be augmented in a "refresh" operation. The refresh operation can combine the
+ // old and the new nonce values. In order to make sure the new parts of the value
+ // are consistent with the old, possibly-reused parts, we check the property value
+ // again after the refresh and do the whole fetch again if the property invalidated
+ // us while we were refreshing.
+ if (cachedResult != null) {
+ final Result refreshedResult = refresh(cachedResult, query);
+ if (refreshedResult != cachedResult) {
+ if (DEBUG) {
+ Log.d(TAG, "cache refresh for " + query);
+ }
+ final long afterRefreshNonce = getCurrentNonce();
+ if (currentNonce != afterRefreshNonce) {
+ currentNonce = afterRefreshNonce;
+ if (DEBUG) {
+ Log.d(TAG, "restarting query because nonce changed in refresh");
+ }
+ continue;
+ }
+ synchronized (mLock) {
+ if (currentNonce != mLastSeenNonce) {
+ // Do nothing: cache is already out of date. Just return the value
+ // we already have: there's no guarantee that the contents of mCache
+ // won't become invalid as soon as we return.
+ } else if (refreshedResult == null) {
+ mCache.remove(query);
+ } else {
+ mCache.put(query, refreshedResult);
+ }
+ }
+ return refreshedResult;
+ }
+ if (DEBUG) {
+ Log.d(TAG, "cache hit for " + query);
+ }
+ return cachedResult;
+ }
+ // Cache miss: make the value from scratch.
+ if (DEBUG) {
+ Log.d(TAG, "cache miss for " + query);
+ }
+ final Result result = recompute(query);
+ synchronized (mLock) {
+ // If someone else invalidated the cache while we did the recomputation, don't
+ // update the cache with a potentially stale result.
+ if (mLastSeenNonce == currentNonce && result != null) {
+ mCache.put(query, result);
+ }
+ }
+ return result;
+ }
+ }
+
+ // Inner class avoids initialization in processes that don't do any invalidation
+ private static final class NoPreloadHolder {
+ private static final AtomicLong sNextNonce = new AtomicLong((new Random()).nextLong());
+ public static long next() {
+ return sNextNonce.getAndIncrement();
+ }
+ }
+
+ /**
+ * Non-static convenience version of disableSystemWide() for situations in which only a
+ * single PropertyInvalidatedCache is keyed on a particular property value.
+ *
+ * When multiple caches share a single property value, using an instance method on one of
+ * the cache objects to invalidate all of the cache objects becomes confusing and you should
+ * just use the static version of this function.
+ */
+ public final void disableSystemWide() {
+ disableSystemWide(mPropertyName);
+ }
+
+ /**
+ * Disable all caches system-wide that are keyed on {@var name}. This
+ * function is synchronous: caches are invalidated and disabled upon return.
+ *
+ * @param name Name of the cache-key property to invalidate
+ */
+ public static void disableSystemWide(@NonNull String name) {
+ SystemProperties.set(name, Long.toString(NONCE_DISABLED));
+ }
+
+ /**
+ * Non-static convenience version of invalidateCache() for situations in which only a single
+ * PropertyInvalidatedCache is keyed on a particular property value.
+ */
+ public final void invalidateCache() {
+ invalidateCache(mPropertyName);
+ }
+
+ /**
+ * Invalidate PropertyInvalidatedCache caches in all processes that are keyed on
+ * {@var name}. This function is synchronous: caches are invalidated upon return.
+ *
+ * @param name Name of the cache-key property to invalidate
+ */
+ public static void invalidateCache(@NonNull String name) {
+ // There's no race here: we don't require that values strictly increase, but instead
+ // only that each is unique in a single runtime-restart session.
+ final long nonce = SystemProperties.getLong(name, NONCE_UNSET);
+ if (nonce == NONCE_DISABLED) {
+ if (DEBUG) {
+ Log.d(TAG, "refusing to invalidate disabled cache: " + name);
+ }
+ return;
+ }
+ long newValue;
+ do {
+ newValue = NoPreloadHolder.next();
+ } while (newValue == NONCE_UNSET || newValue == NONCE_DISABLED);
+ final String newValueString = Long.toString(newValue);
+ if (DEBUG) {
+ Log.d(TAG,
+ String.format("invalidating cache [%s]: [%s] -> [%s]",
+ name,
+ nonce,
+ newValueString));
+ }
+ SystemProperties.set(name, newValueString);
+ }
+}
diff --git a/core/java/android/app/job/JobInfo.java b/core/java/android/app/job/JobInfo.java
index 72eea84..7e8a7de 100644
--- a/core/java/android/app/job/JobInfo.java
+++ b/core/java/android/app/job/JobInfo.java
@@ -625,10 +625,6 @@
return hasLateConstraint;
}
- private static boolean kindofEqualsBundle(BaseBundle a, BaseBundle b) {
- return (a == b) || (a != null && a.kindofEquals(b));
- }
-
@Override
public boolean equals(Object o) {
if (!(o instanceof JobInfo)) {
@@ -639,11 +635,11 @@
return false;
}
// XXX won't be correct if one is parcelled and the other not.
- if (!kindofEqualsBundle(extras, j.extras)) {
+ if (!BaseBundle.kindofEquals(extras, j.extras)) {
return false;
}
// XXX won't be correct if one is parcelled and the other not.
- if (!kindofEqualsBundle(transientExtras, j.transientExtras)) {
+ if (!BaseBundle.kindofEquals(transientExtras, j.transientExtras)) {
return false;
}
// XXX for now we consider two different clip data objects to be different,
diff --git a/core/java/android/bluetooth/BluetoothDevice.java b/core/java/android/bluetooth/BluetoothDevice.java
index 5b60b85..838a3b9 100644
--- a/core/java/android/bluetooth/BluetoothDevice.java
+++ b/core/java/android/bluetooth/BluetoothDevice.java
@@ -1025,7 +1025,11 @@
try {
String name = service.getRemoteName(this);
if (name != null) {
- return name.replaceAll("[\\t\\n\\r]+", " ");
+ // remove whitespace characters from the name
+ return name
+ .replace('\t', ' ')
+ .replace('\n', ' ')
+ .replace('\r', ' ');
}
return null;
} catch (RemoteException e) {
diff --git a/core/java/android/bluetooth/BluetoothHidHost.java b/core/java/android/bluetooth/BluetoothHidHost.java
index 26e3e27..e9e1f68 100644
--- a/core/java/android/bluetooth/BluetoothHidHost.java
+++ b/core/java/android/bluetooth/BluetoothHidHost.java
@@ -18,7 +18,6 @@
import android.Manifest;
import android.annotation.NonNull;
-import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
@@ -330,6 +329,7 @@
* {@inheritDoc}
*/
@Override
+ @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
public @NonNull List<BluetoothDevice> getConnectedDevices() {
if (VDBG) log("getConnectedDevices()");
final IBluetoothHidHost service = getService();
@@ -370,8 +370,12 @@
* {@inheritDoc}
*/
@Override
- public int getConnectionState(@Nullable BluetoothDevice device) {
+ @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+ public int getConnectionState(@NonNull BluetoothDevice device) {
if (VDBG) log("getState(" + device + ")");
+ if (device == null) {
+ throw new IllegalArgumentException("device must not be null");
+ }
final IBluetoothHidHost service = getService();
if (service != null && isEnabled() && isValidDevice(device)) {
try {
@@ -416,9 +420,12 @@
*/
@SystemApi
@RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
- public boolean setConnectionPolicy(@Nullable BluetoothDevice device,
+ public boolean setConnectionPolicy(@NonNull BluetoothDevice device,
@ConnectionPolicy int connectionPolicy) {
if (DBG) log("setConnectionPolicy(" + device + ", " + connectionPolicy + ")");
+ if (device == null) {
+ throw new IllegalArgumentException("device must not be null");
+ }
final IBluetoothHidHost service = getService();
if (service != null && isEnabled() && isValidDevice(device)) {
if (connectionPolicy != BluetoothProfile.CONNECTION_POLICY_FORBIDDEN
@@ -465,8 +472,11 @@
*/
@SystemApi
@RequiresPermission(Manifest.permission.BLUETOOTH)
- public @ConnectionPolicy int getConnectionPolicy(@Nullable BluetoothDevice device) {
+ public @ConnectionPolicy int getConnectionPolicy(@NonNull BluetoothDevice device) {
if (VDBG) log("getConnectionPolicy(" + device + ")");
+ if (device == null) {
+ throw new IllegalArgumentException("device must not be null");
+ }
final IBluetoothHidHost service = getService();
if (service != null && isEnabled() && isValidDevice(device)) {
try {
diff --git a/core/java/android/bluetooth/BluetoothMap.java b/core/java/android/bluetooth/BluetoothMap.java
index 1c62faa..cc2b615 100644
--- a/core/java/android/bluetooth/BluetoothMap.java
+++ b/core/java/android/bluetooth/BluetoothMap.java
@@ -27,6 +27,7 @@
import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
+import android.util.CloseGuard;
import android.util.Log;
import java.util.ArrayList;
@@ -39,12 +40,14 @@
* @hide
*/
@SystemApi
-public final class BluetoothMap implements BluetoothProfile {
+public final class BluetoothMap implements BluetoothProfile, AutoCloseable {
private static final String TAG = "BluetoothMap";
private static final boolean DBG = true;
private static final boolean VDBG = false;
+ private CloseGuard mCloseGuard;
+
/** @hide */
@SuppressLint("ActionValue")
@SystemApi
@@ -86,15 +89,16 @@
if (DBG) Log.d(TAG, "Create BluetoothMap proxy object");
mAdapter = BluetoothAdapter.getDefaultAdapter();
mProfileConnector.connect(context, listener);
+ mCloseGuard = new CloseGuard();
+ mCloseGuard.open("close");
}
- @SuppressLint("GenericException")
- protected void finalize() throws Throwable {
- try {
- close();
- } finally {
- super.finalize();
+ @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+ protected void finalize() {
+ if (mCloseGuard != null) {
+ mCloseGuard.warnIfOpen();
}
+ close();
}
/**
@@ -105,7 +109,10 @@
*
* @hide
*/
- public synchronized void close() {
+ @SystemApi
+ @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
+ public void close() {
+ if (VDBG) log("close()");
mProfileConnector.disconnect();
}
@@ -250,6 +257,7 @@
* @hide
*/
@SystemApi
+ @RequiresPermission(Manifest.permission.BLUETOOTH_PRIVILEGED)
public @NonNull List<BluetoothDevice> getConnectedDevices() {
if (DBG) log("getConnectedDevices()");
final IBluetoothMap service = getService();
diff --git a/core/java/android/bluetooth/BluetoothPbap.java b/core/java/android/bluetooth/BluetoothPbap.java
index 1f89ddf..277a5a8 100644
--- a/core/java/android/bluetooth/BluetoothPbap.java
+++ b/core/java/android/bluetooth/BluetoothPbap.java
@@ -278,16 +278,19 @@
}
/**
- * Pbap does not store connection policy, so this function only disconnects pbap if
- * connectionPolicy is {@link #CONNECTION_POLICY_FORBIDDEN}.
+ * Set connection policy of the profile and tries to disconnect it if connectionPolicy is
+ * {@link BluetoothProfile#CONNECTION_POLICY_FORBIDDEN}
*
* <p> The device should already be paired.
- * Connection policy can be one of {@link #CONNECTION_POLICY_ALLOWED},
- * {@link #CONNECTION_POLICY_FORBIDDEN}, {@link #CONNECTION_POLICY_UNKNOWN}
+ * Connection policy can be one of:
+ * {@link BluetoothProfile#CONNECTION_POLICY_ALLOWED},
+ * {@link BluetoothProfile#CONNECTION_POLICY_FORBIDDEN},
+ * {@link BluetoothProfile#CONNECTION_POLICY_UNKNOWN}
*
* @param device Paired bluetooth device
- * @param connectionPolicy determines whether to disconnect the device
- * @return true if pbap is successfully disconnected, false otherwise
+ * @param connectionPolicy is the connection policy to set to for this profile
+ * @return true if connectionPolicy is set, false on error
+ *
* @hide
*/
@SystemApi
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 4527d8e..8794173 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -3356,6 +3356,7 @@
CONSUMER_IR_SERVICE,
//@hide: TRUST_SERVICE,
TV_INPUT_SERVICE,
+ //@hide: TV_TUNER_RESOURCE_MGR_SERVICE,
//@hide: NETWORK_SCORE_SERVICE,
USAGE_STATS_SERVICE,
MEDIA_SESSION_SERVICE,
@@ -4586,6 +4587,17 @@
public static final String TV_INPUT_SERVICE = "tv_input";
/**
+ * Use with {@link #getSystemService(String)} to retrieve a
+ * {@link android.media.tv.tunerresourcemanager.TunerResourceManager} for interacting with TV
+ * tuner resources on the device.
+ *
+ * @see #getSystemService(String)
+ * @see android.media.tv.TunerResourceManager
+ * @hide
+ */
+ public static final String TV_TUNER_RESOURCE_MGR_SERVICE = "tv_tuner_resource_mgr";
+
+ /**
* {@link android.net.NetworkScoreManager} for managing network scoring.
* @see #getSystemService(String)
* @see android.net.NetworkScoreManager
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index d859a3af..99ca048 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -4515,12 +4515,6 @@
public static final String EXTRA_LTE_EARFCN_RSRP_BOOST = "LteEarfcnRsrpBoost";
/**
- * An parcelable extra used with {@link #ACTION_SERVICE_STATE} representing the service state.
- * @hide
- */
- public static final String EXTRA_SERVICE_STATE = "android.intent.extra.SERVICE_STATE";
-
- /**
* The name of the extra used to define the text to be processed, as a
* CharSequence. Note that this may be a styled CharSequence, so you must use
* {@link Bundle#getCharSequence(String) Bundle.getCharSequence()} to retrieve it.
@@ -6632,7 +6626,7 @@
this.mClipData = new ClipData(o.mClipData);
}
} else {
- if (o.mExtras != null && !o.mExtras.maybeIsEmpty()) {
+ if (o.mExtras != null && !o.mExtras.isDefinitelyEmpty()) {
this.mExtras = Bundle.STRIPPED;
}
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index 429a6e5..6d051e4 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -529,19 +529,12 @@
* Notify the package manager that a list of dex files have been loaded.
*
* @param loadingPackageName the name of the package who performs the load
- * @param classLoadersNames the names of the class loaders present in the loading chain. The
- * list encodes the class loader chain in the natural order. The first class loader has
- * the second one as its parent and so on. The dex files present in the class path of the
- * first class loader will be recorded in the usage file.
- * @param classPaths the class paths corresponding to the class loaders names from
- * {@param classLoadersNames}. The the first element corresponds to the first class loader
- * and so on. A classpath is represented as a list of dex files separated by
- * {@code File.pathSeparator}, or null if the class loader's classpath is not known.
- * The dex files found in the first class path will be recorded in the usage file.
+ * @param classLoaderContextMap a map from file paths to dex files that have been loaded to
+ * the class loader context that was used to load them.
* @param loaderIsa the ISA of the loader process
*/
- oneway void notifyDexLoad(String loadingPackageName, in List<String> classLoadersNames,
- in List<String> classPaths, String loaderIsa);
+ oneway void notifyDexLoad(String loadingPackageName,
+ in Map<String, String> classLoaderContextMap, String loaderIsa);
/**
* Register an application dex module with the package manager.
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 93b90b4..2db661d 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -2147,6 +2147,23 @@
/**
* Feature for {@link #getSystemAvailableFeatures} and
+ * {@link #hasSystemFeature(String, int)}: If this feature is supported, the feature version
+ * specifies a date such that the device is known to pass the Vulkan dEQP test suite associated
+ * with that date. The date is encoded as follows:
+ * <ul>
+ * <li>Year in bits 31-16</li>
+ * <li>Month in bits 15-8</li>
+ * <li>Day in bits 7-0</li>
+ * </ul>
+ * <p>
+ * Example: 2019-03-01 is encoded as 0x07E30301, and would indicate that the device passes the
+ * Vulkan dEQP test suite version that was current on 2019-03-01.
+ */
+ @SdkConstant(SdkConstantType.FEATURE)
+ public static final String FEATURE_VULKAN_DEQP_LEVEL = "android.software.vulkan.deqp.level";
+
+ /**
+ * Feature for {@link #getSystemAvailableFeatures} and
* {@link #hasSystemFeature}: The device includes broadcast radio tuner.
* @hide
*/
@@ -2867,6 +2884,13 @@
public static final String FEATURE_IPSEC_TUNNELS = "android.software.ipsec_tunnels";
/**
+ * Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}:
+ * The device does not have a slices implementation.
+ * @hide
+ */
+ @SdkConstant(SdkConstantType.FEATURE)
+ public static final String FEATURE_SLICES_DISABLED = "android.software.slices_disabled";
+ /**
* Extra field name for the URI to a verification file. Passed to a package
* verifier.
*
diff --git a/core/java/android/debug/AdbManager.java b/core/java/android/debug/AdbManager.java
index 0a76bed..7714dd8 100644
--- a/core/java/android/debug/AdbManager.java
+++ b/core/java/android/debug/AdbManager.java
@@ -31,6 +31,114 @@
public class AdbManager {
private static final String TAG = "AdbManager";
+ /**
+ * Action indicating the state change of wireless debugging. Can be either
+ * STATUS_CONNECTED
+ * STATUS_DISCONNECTED
+ *
+ * @hide
+ */
+ public static final String WIRELESS_DEBUG_STATE_CHANGED_ACTION =
+ "com.android.server.adb.WIRELESS_DEBUG_STATUS";
+
+ /**
+ * Contains the list of paired devices.
+ *
+ * @hide
+ */
+ public static final String WIRELESS_DEBUG_PAIRED_DEVICES_ACTION =
+ "com.android.server.adb.WIRELESS_DEBUG_PAIRED_DEVICES";
+
+ /**
+ * Action indicating the status of a pairing. Can be either
+ * WIRELESS_STATUS_FAIL
+ * WIRELESS_STATUS_SUCCESS
+ * WIRELESS_STATUS_CANCELLED
+ * WIRELESS_STATUS_PAIRING_CODE
+ * WIRELESS_STATUS_CONNECTED
+ *
+ * @hide
+ */
+ public static final String WIRELESS_DEBUG_PAIRING_RESULT_ACTION =
+ "com.android.server.adb.WIRELESS_DEBUG_PAIRING_RESULT";
+
+ /**
+ * Extra containing the PairDevice map of paired/pairing devices.
+ *
+ * @hide
+ */
+ public static final String WIRELESS_DEVICES_EXTRA = "devices_map";
+
+ /**
+ * The status of the pairing/unpairing.
+ *
+ * @hide
+ */
+ public static final String WIRELESS_STATUS_EXTRA = "status";
+
+ /**
+ * The PairDevice.
+ *
+ * @hide
+ */
+ public static final String WIRELESS_PAIR_DEVICE_EXTRA = "pair_device";
+
+ /**
+ * The six-digit pairing code.
+ *
+ * @hide
+ */
+ public static final String WIRELESS_PAIRING_CODE_EXTRA = "pairing_code";
+
+ /**
+ * The adb connection/pairing port that was opened.
+ *
+ * @hide
+ */
+ public static final String WIRELESS_DEBUG_PORT_EXTRA = "adb_port";
+
+ /**
+ * Status indicating the pairing/unpairing failed.
+ *
+ * @hide
+ */
+ public static final int WIRELESS_STATUS_FAIL = 0;
+
+ /**
+ * Status indicating the pairing/unpairing succeeded.
+ *
+ * @hide
+ */
+ public static final int WIRELESS_STATUS_SUCCESS = 1;
+
+ /**
+ * Status indicating the pairing/unpairing was cancelled.
+ *
+ * @hide
+ */
+ public static final int WIRELESS_STATUS_CANCELLED = 2;
+
+ /**
+ * Status indicating the pairing code for pairing.
+ *
+ * @hide
+ */
+ public static final int WIRELESS_STATUS_PAIRING_CODE = 3;
+
+ /**
+ * Status indicating wireless debugging is connected.
+ *
+ * @hide
+ */
+ public static final int WIRELESS_STATUS_CONNECTED = 4;
+
+ /**
+ * Status indicating wireless debugging is disconnected.
+ *
+ * @hide
+ */
+ public static final int WIRELESS_STATUS_DISCONNECTED = 5;
+
private final Context mContext;
private final IAdbManager mService;
diff --git a/core/java/android/debug/AdbManagerInternal.java b/core/java/android/debug/AdbManagerInternal.java
index 51eb7fc..d730129 100644
--- a/core/java/android/debug/AdbManagerInternal.java
+++ b/core/java/android/debug/AdbManagerInternal.java
@@ -42,7 +42,7 @@
/**
* Returns {@code true} if ADB debugging is enabled.
*/
- public abstract boolean isAdbEnabled();
+ public abstract boolean isAdbEnabled(byte transportType);
/**
* Returns the file that contains all of the ADB keys used by the device.
@@ -53,4 +53,14 @@
* Returns the file that contains all of the ADB keys and their last used time.
*/
public abstract File getAdbTempKeysFile();
+
+ /**
+ * Starts adbd for a transport.
+ */
+ public abstract void startAdbdForTransport(byte transportType);
+
+ /**
+ * Stops adbd for a transport.
+ */
+ public abstract void stopAdbdForTransport(byte transportType);
}
diff --git a/telephony/java/android/telephony/ims/RcsMessageQueryParams.aidl b/core/java/android/debug/AdbTransportType.aidl
similarity index 70%
rename from telephony/java/android/telephony/ims/RcsMessageQueryParams.aidl
rename to core/java/android/debug/AdbTransportType.aidl
index e9cbd9c..6904615 100644
--- a/telephony/java/android/telephony/ims/RcsMessageQueryParams.aidl
+++ b/core/java/android/debug/AdbTransportType.aidl
@@ -1,12 +1,11 @@
/*
- *
- * Copyright 2019, The Android Open Source Project
+ * Copyright (C) 2019 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
+ * 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,
@@ -15,6 +14,12 @@
* limitations under the License.
*/
-package android.telephony.ims;
+package android.debug;
-parcelable RcsMessageQueryParams;
+/** @hide */
+@Backing(type="byte")
+enum AdbTransportType {
+ USB,
+ WIFI,
+}
+
diff --git a/core/java/android/debug/IAdbManager.aidl b/core/java/android/debug/IAdbManager.aidl
index c48fc07..aea7633 100644
--- a/core/java/android/debug/IAdbManager.aidl
+++ b/core/java/android/debug/IAdbManager.aidl
@@ -43,6 +43,62 @@
void clearDebuggingKeys();
/**
+ * Allow ADB wireless debugging on the connected network. If {@code alwaysAllow}
+ * is {@code true}, add {@code bssid} to list of networks that the user has
+ * approved.
+ *
+ * @param alwaysAllow if true, add permanently to list of allowed networks
+ * @param bssid BSSID of the network
+ */
+ void allowWirelessDebugging(boolean alwaysAllow, String bssid);
+
+ /**
+ * Deny ADB wireless debugging on the connected network.
+ */
+ void denyWirelessDebugging();
+
+ /**
+ * Returns a Map<String, PairDevice> with the key fingerprint mapped to the device information.
+ */
+ Map getPairedDevices();
+
+ /**
+ * Unpair the device identified by the key fingerprint it uses.
+ *
+ * @param fingerprint fingerprint of the key the device is using.
+ */
+ void unpairDevice(String fingerprint);
+
+ /**
+ * Enables pairing by pairing code. The result of the enable will be sent via intent action
+ * {@link android.debug.AdbManager#WIRELESS_DEBUG_ENABLE_DISCOVER_ACTION}. Furthermore, the
+ * pairing code will also be sent in the intent as an extra
+ * @{link android.debug.AdbManager#WIRELESS_PAIRING_CODE_EXTRA}. Note that only one
+ * pairing method can be enabled at a time, either by pairing code, or by QR code.
+ */
+ void enablePairingByPairingCode();
+
+ /**
+ * Enables pairing by QR code. The result of the enable will be sent via intent action
+ * {@link android.debug.AdbManager#WIRELESS_DEBUG_ENABLE_DISCOVER_ACTION}. Note that only one
+ * pairing method can be enabled at a time, either by pairing code, or by QR code.
+ *
+ * @param serviceName The MDNS service name parsed from the QR code.
+ * @param password The password parsed from the QR code.
+ */
+ void enablePairingByQrCode(String serviceName, String password);
+
+ /**
+ * Returns the network port that adb wireless server is running on.
+ */
+ int getAdbWirelessPort();
+
+ /**
+ * Disables pairing.
+ */
+ void disablePairing();
+
+ /**
* Returns true if device supports secure Adb over Wi-Fi.
*/
boolean isAdbWifiSupported();
diff --git a/core/java/android/debug/IAdbTransport.aidl b/core/java/android/debug/IAdbTransport.aidl
index 77211fc93..f018813 100644
--- a/core/java/android/debug/IAdbTransport.aidl
+++ b/core/java/android/debug/IAdbTransport.aidl
@@ -16,7 +16,9 @@
package android.debug;
+import android.debug.AdbTransportType;
+
/** @hide */
interface IAdbTransport {
- void onAdbEnabled(boolean enabled);
+ void onAdbEnabled(boolean enabled, in AdbTransportType type);
}
diff --git a/core/java/android/debug/PairDevice.java b/core/java/android/debug/PairDevice.java
new file mode 100644
index 0000000..2d5b446
--- /dev/null
+++ b/core/java/android/debug/PairDevice.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.debug;
+
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.annotations.Immutable;
+import com.android.internal.util.Preconditions;
+
+/**
+ * Contains information about the client in an ADB connection.
+ * @hide
+ */
+@Immutable
+public class PairDevice implements Parcelable {
+ /**
+ * The human-readable name of the device.
+ */
+ @NonNull private final String mName;
+
+ /**
+ * The device's guid.
+ */
+ @NonNull private final String mGuid;
+
+ /**
+ * Indicates whether the device is currently connected to adbd.
+ */
+ private final boolean mConnected;
+
+ public PairDevice(@NonNull String name, @NonNull String guid, boolean connected) {
+ Preconditions.checkStringNotEmpty(name);
+ Preconditions.checkStringNotEmpty(guid);
+ mName = name;
+ mGuid = guid;
+ mConnected = connected;
+ }
+
+ /**
+ * @return the device name.
+ */
+ @NonNull
+ public String getDeviceName() {
+ return mName;
+ }
+
+ /**
+ * @return the device GUID.
+ */
+ @NonNull
+ public String getGuid() {
+ return mGuid;
+ }
+
+ /**
+ * @return the adb connection state of the device.
+ */
+ public boolean isConnected() {
+ return mConnected;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeString(mName);
+ dest.writeString(mGuid);
+ dest.writeBoolean(mConnected);
+ }
+
+ /**
+ * @return Human-readable info about the object.
+ */
+ @Override
+ public String toString() {
+ return "\n" + mName + "\n" + mGuid + "\n" + mConnected;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @NonNull
+ public static final Parcelable.Creator<PairDevice> CREATOR =
+ new Creator<PairDevice>() {
+ @Override
+ public PairDevice createFromParcel(Parcel source) {
+ return new PairDevice(source.readString(), source.readString(),
+ source.readBoolean());
+ }
+
+ @Override
+ public PairDevice[] newArray(int size) {
+ return new PairDevice[size];
+ }
+ };
+}
diff --git a/core/java/android/hardware/usb/UsbManager.java b/core/java/android/hardware/usb/UsbManager.java
index b0d0b4c..f540bfb 100644
--- a/core/java/android/hardware/usb/UsbManager.java
+++ b/core/java/android/hardware/usb/UsbManager.java
@@ -18,6 +18,7 @@
package android.hardware.usb;
import android.Manifest;
+import android.annotation.LongDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresFeature;
@@ -337,12 +338,14 @@
* Code for the mtp usb function. Passed as a mask into {@link #setCurrentFunctions(long)}
* {@hide}
*/
+ @SystemApi
public static final long FUNCTION_MTP = GadgetFunction.MTP;
/**
* Code for the ptp usb function. Passed as a mask into {@link #setCurrentFunctions(long)}
* {@hide}
*/
+ @SystemApi
public static final long FUNCTION_PTP = GadgetFunction.PTP;
/**
@@ -356,24 +359,28 @@
* Code for the midi usb function. Passed as a mask into {@link #setCurrentFunctions(long)}
* {@hide}
*/
+ @SystemApi
public static final long FUNCTION_MIDI = GadgetFunction.MIDI;
/**
* Code for the accessory usb function.
* {@hide}
*/
+ @SystemApi
public static final long FUNCTION_ACCESSORY = GadgetFunction.ACCESSORY;
/**
* Code for the audio source usb function.
* {@hide}
*/
+ @SystemApi
public static final long FUNCTION_AUDIO_SOURCE = GadgetFunction.AUDIO_SOURCE;
/**
* Code for the adb usb function.
* {@hide}
*/
+ @SystemApi
public static final long FUNCTION_ADB = GadgetFunction.ADB;
/**
@@ -399,6 +406,20 @@
FUNCTION_NAME_TO_CODE.put(UsbManager.USB_FUNCTION_NCM, FUNCTION_NCM);
}
+ /** @hide */
+ @LongDef(flag = true, prefix = { "FUNCTION_" }, value = {
+ FUNCTION_NONE,
+ FUNCTION_MTP,
+ FUNCTION_PTP,
+ FUNCTION_RNDIS,
+ FUNCTION_MIDI,
+ FUNCTION_ACCESSORY,
+ FUNCTION_AUDIO_SOURCE,
+ FUNCTION_ADB,
+ FUNCTION_NCM,
+ })
+ public @interface UsbFunctionMode {}
+
private final Context mContext;
private final IUsbManager mService;
@@ -721,7 +742,7 @@
*/
@SystemApi
@RequiresPermission(Manifest.permission.MANAGE_USB)
- public void setCurrentFunctions(long functions) {
+ public void setCurrentFunctions(@UsbFunctionMode long functions) {
try {
mService.setCurrentFunctions(functions);
} catch (RemoteException e) {
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index cb31404..589b1aa 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -1279,7 +1279,8 @@
@UnsupportedAppUsage
public NetworkCapabilities[] getDefaultNetworkCapabilitiesForUser(int userId) {
try {
- return mService.getDefaultNetworkCapabilitiesForUser(userId);
+ return mService.getDefaultNetworkCapabilitiesForUser(
+ userId, mContext.getOpPackageName());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -1361,7 +1362,7 @@
@Nullable
public NetworkCapabilities getNetworkCapabilities(@Nullable Network network) {
try {
- return mService.getNetworkCapabilities(network);
+ return mService.getNetworkCapabilities(network, mContext.getOpPackageName());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -2424,14 +2425,14 @@
/**
* Get the set of tethered dhcp ranges.
*
- * @return an array of 0 or more {@code String} of tethered dhcp ranges.
- * @deprecated This API just return the default value which is not used in DhcpServer.
+ * @deprecated This method is not supported.
+ * TODO: remove this function when all of clients are removed.
* {@hide}
*/
@RequiresPermission(android.Manifest.permission.NETWORK_SETTINGS)
@Deprecated
public String[] getTetheredDhcpRanges() {
- return getTetheringManager().getTetheredDhcpRanges();
+ throw new UnsupportedOperationException("getTetheredDhcpRanges is not supported");
}
/**
@@ -4035,10 +4036,9 @@
@NonNull PendingIntent operation) {
printStackTrace();
checkPendingIntentNotNull(operation);
- final String callingPackageName = mContext.getOpPackageName();
try {
mService.pendingRequestForNetwork(
- request.networkCapabilities, operation, callingPackageName);
+ request.networkCapabilities, operation, mContext.getOpPackageName());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
} catch (ServiceSpecificException e) {
@@ -4150,10 +4150,9 @@
@NonNull PendingIntent operation) {
printStackTrace();
checkPendingIntentNotNull(operation);
- final String callingPackageName = mContext.getOpPackageName();
try {
mService.pendingListenForNetwork(
- request.networkCapabilities, operation, callingPackageName);
+ request.networkCapabilities, operation, mContext.getOpPackageName());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
} catch (ServiceSpecificException e) {
@@ -4716,19 +4715,19 @@
/**
* Returns the {@code uid} of the owner of a network connection.
*
- * @param protocol The protocol of the connection. Only {@code IPPROTO_TCP} and
- * {@code IPPROTO_UDP} currently supported.
+ * @param protocol The protocol of the connection. Only {@code IPPROTO_TCP} and {@code
+ * IPPROTO_UDP} currently supported.
* @param local The local {@link InetSocketAddress} of a connection.
* @param remote The remote {@link InetSocketAddress} of a connection.
- *
* @return {@code uid} if the connection is found and the app has permission to observe it
- * (e.g., if it is associated with the calling VPN app's tunnel) or
- * {@link android.os.Process#INVALID_UID} if the connection is not found.
- * Throws {@link SecurityException} if the caller is not the active VPN for the current user.
- * Throws {@link IllegalArgumentException} if an unsupported protocol is requested.
+ * (e.g., if it is associated with the calling VPN app's VpnService tunnel) or {@link
+ * android.os.Process#INVALID_UID} if the connection is not found.
+ * @throws {@link SecurityException} if the caller is not the active VpnService for the current
+ * user.
+ * @throws {@link IllegalArgumentException} if an unsupported protocol is requested.
*/
- public int getConnectionOwnerUid(int protocol, @NonNull InetSocketAddress local,
- @NonNull InetSocketAddress remote) {
+ public int getConnectionOwnerUid(
+ int protocol, @NonNull InetSocketAddress local, @NonNull InetSocketAddress remote) {
ConnectionInfo connectionInfo = new ConnectionInfo(protocol, local, remote);
try {
return mService.getConnectionOwnerUid(connectionInfo);
diff --git a/core/java/android/net/EthernetManager.java b/core/java/android/net/EthernetManager.java
index a3899b7..139f5be 100644
--- a/core/java/android/net/EthernetManager.java
+++ b/core/java/android/net/EthernetManager.java
@@ -28,6 +28,7 @@
import java.util.ArrayList;
import java.util.Objects;
+import java.util.concurrent.Executor;
/**
* A class representing the IP configuration of the Ethernet network.
@@ -248,18 +249,19 @@
* @param callback A callback to be called once the request has been fulfilled.
*/
@NonNull
- public TetheredInterfaceRequest requestTetheredInterface(
- @NonNull TetheredInterfaceCallback callback) {
+ public TetheredInterfaceRequest requestTetheredInterface(@NonNull final Executor executor,
+ @NonNull final TetheredInterfaceCallback callback) {
Objects.requireNonNull(callback, "Callback must be non-null");
+ Objects.requireNonNull(executor, "Executor must be non-null");
final ITetheredInterfaceCallback cbInternal = new ITetheredInterfaceCallback.Stub() {
@Override
public void onAvailable(String iface) {
- callback.onAvailable(iface);
+ executor.execute(() -> callback.onAvailable(iface));
}
@Override
public void onUnavailable() {
- callback.onUnavailable();
+ executor.execute(() -> callback.onUnavailable());
}
};
diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl
index 3a55461..1434560 100644
--- a/core/java/android/net/IConnectivityManager.aidl
+++ b/core/java/android/net/IConnectivityManager.aidl
@@ -59,7 +59,8 @@
NetworkInfo[] getAllNetworkInfo();
Network getNetworkForType(int networkType);
Network[] getAllNetworks();
- NetworkCapabilities[] getDefaultNetworkCapabilitiesForUser(int userId);
+ NetworkCapabilities[] getDefaultNetworkCapabilitiesForUser(
+ int userId, String callingPackageName);
boolean isNetworkSupported(int networkType);
@@ -68,7 +69,7 @@
LinkProperties getLinkPropertiesForType(int networkType);
LinkProperties getLinkProperties(in Network network);
- NetworkCapabilities getNetworkCapabilities(in Network network);
+ NetworkCapabilities getNetworkCapabilities(in Network network, String callingPackageName);
@UnsupportedAppUsage
NetworkState[] getAllNetworkState();
diff --git a/core/java/android/net/Ikev2VpnProfile.java b/core/java/android/net/Ikev2VpnProfile.java
index 42b4da1..f19a341 100644
--- a/core/java/android/net/Ikev2VpnProfile.java
+++ b/core/java/android/net/Ikev2VpnProfile.java
@@ -25,7 +25,10 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.os.Process;
import android.security.Credentials;
+import android.security.KeyStore;
+import android.security.keystore.AndroidKeyStoreProvider;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.net.VpnProfile;
@@ -59,6 +62,11 @@
* Exchange, Version 2 (IKEv2)</a>
*/
public final class Ikev2VpnProfile extends PlatformVpnProfile {
+ /** Prefix for when a Private Key is an alias to look for in KeyStore @hide */
+ public static final String PREFIX_KEYSTORE_ALIAS = "KEYSTORE_ALIAS:";
+ /** Prefix for when a Private Key is stored directly in the profile @hide */
+ public static final String PREFIX_INLINE = "INLINE:";
+
private static final String MISSING_PARAM_MSG_TMPL = "Required parameter was not provided: %s";
private static final String EMPTY_CERT = "";
@@ -339,7 +347,8 @@
break;
case TYPE_IKEV2_IPSEC_RSA:
profile.ipsecUserCert = certificateToPemString(mUserCert);
- profile.ipsecSecret = encodeForIpsecSecret(mRsaPrivateKey.getEncoded());
+ profile.ipsecSecret =
+ PREFIX_INLINE + encodeForIpsecSecret(mRsaPrivateKey.getEncoded());
profile.ipsecCaCert =
mServerRootCaCert == null ? "" : certificateToPemString(mServerRootCaCert);
break;
@@ -360,6 +369,22 @@
@NonNull
public static Ikev2VpnProfile fromVpnProfile(@NonNull VpnProfile profile)
throws IOException, GeneralSecurityException {
+ return fromVpnProfile(profile, null);
+ }
+
+ /**
+ * Builds the Ikev2VpnProfile from the given profile.
+ *
+ * @param profile the source VpnProfile to build from
+ * @param keyStore the Android Keystore instance to use to retrieve the private key, or null if
+ * the private key is PEM-encoded into the profile.
+ * @return The IKEv2/IPsec VPN profile
+ * @hide
+ */
+ @NonNull
+ public static Ikev2VpnProfile fromVpnProfile(
+ @NonNull VpnProfile profile, @Nullable KeyStore keyStore)
+ throws IOException, GeneralSecurityException {
final Builder builder = new Builder(profile.server, profile.ipsecIdentifier);
builder.setProxy(profile.proxy);
builder.setAllowedAlgorithms(profile.getAllowedAlgorithms());
@@ -378,8 +403,21 @@
builder.setAuthPsk(decodeFromIpsecSecret(profile.ipsecSecret));
break;
case TYPE_IKEV2_IPSEC_RSA:
+ final PrivateKey key;
+ if (profile.ipsecSecret.startsWith(PREFIX_KEYSTORE_ALIAS)) {
+ Objects.requireNonNull(keyStore, "Missing Keystore for aliased PrivateKey");
+
+ final String alias =
+ profile.ipsecSecret.substring(PREFIX_KEYSTORE_ALIAS.length());
+ key = AndroidKeyStoreProvider.loadAndroidKeyStorePrivateKeyFromKeystore(
+ keyStore, alias, Process.myUid());
+ } else if (profile.ipsecSecret.startsWith(PREFIX_INLINE)) {
+ key = getPrivateKey(profile.ipsecSecret.substring(PREFIX_INLINE.length()));
+ } else {
+ throw new IllegalArgumentException("Invalid RSA private key prefix");
+ }
+
final X509Certificate userCert = certificateFromPemString(profile.ipsecUserCert);
- final PrivateKey key = getPrivateKey(profile.ipsecSecret);
final X509Certificate serverRootCa = certificateFromPemString(profile.ipsecCaCert);
builder.setAuthDigitalSignature(userCert, key, serverRootCa);
break;
@@ -391,6 +429,39 @@
}
/**
+ * Validates that the VpnProfile is acceptable for the purposes of an Ikev2VpnProfile.
+ *
+ * @hide
+ */
+ public static boolean isValidVpnProfile(@NonNull VpnProfile profile) {
+ if (profile.server.isEmpty() || profile.ipsecIdentifier.isEmpty()) {
+ return false;
+ }
+
+ switch (profile.type) {
+ case TYPE_IKEV2_IPSEC_USER_PASS:
+ if (profile.username.isEmpty() || profile.password.isEmpty()) {
+ return false;
+ }
+ break;
+ case TYPE_IKEV2_IPSEC_PSK:
+ if (profile.ipsecSecret.isEmpty()) {
+ return false;
+ }
+ break;
+ case TYPE_IKEV2_IPSEC_RSA:
+ if (profile.ipsecSecret.isEmpty() || profile.ipsecUserCert.isEmpty()) {
+ return false;
+ }
+ break;
+ default:
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
* Converts a X509 Certificate to a PEM-formatted string.
*
* <p>Must be public due to runtime-package restrictions.
@@ -432,7 +503,6 @@
/** @hide */
@NonNull
- @VisibleForTesting(visibility = Visibility.PRIVATE)
public static String encodeForIpsecSecret(@NonNull byte[] secret) {
checkNotNull(secret, MISSING_PARAM_MSG_TMPL, "secret");
diff --git a/core/java/android/net/NetworkAgent.java b/core/java/android/net/NetworkAgent.java
index 61a1484..c063b39 100644
--- a/core/java/android/net/NetworkAgent.java
+++ b/core/java/android/net/NetworkAgent.java
@@ -33,6 +33,7 @@
import com.android.internal.util.Protocol;
import java.util.ArrayList;
+import java.util.Objects;
import java.util.concurrent.atomic.AtomicBoolean;
/**
@@ -53,6 +54,12 @@
@NonNull
public final Network network;
+ // Whether this NetworkAgent is using the legacy (never unhidden) API. The difference is
+ // that the legacy API uses NetworkInfo to convey the state, while the current API is
+ // exposing methods to manage it and generate it internally instead.
+ // TODO : remove this as soon as all agents have been converted.
+ private final boolean mIsLegacy;
+
private final Handler mHandler;
private volatile AsyncChannel mAsyncChannel;
private final String LOG_TAG;
@@ -64,6 +71,10 @@
private static final long BW_REFRESH_MIN_WIN_MS = 500;
private boolean mBandwidthUpdateScheduled = false;
private AtomicBoolean mBandwidthUpdatePending = new AtomicBoolean(false);
+ // Not used by legacy agents. Non-legacy agents use this to convert the NetworkAgent system API
+ // into the internal API of ConnectivityService.
+ @NonNull
+ private NetworkInfo mNetworkInfo;
/**
* The ID of the {@link NetworkProvider} that created this object, or
@@ -284,13 +295,16 @@
public NetworkAgent(Looper looper, Context context, String logTag, NetworkInfo ni,
NetworkCapabilities nc, LinkProperties lp, int score, NetworkAgentConfig config,
int providerId) {
- this(looper, context, logTag, nc, lp, score, config, providerId, ni);
+ this(looper, context, logTag, nc, lp, score, config, providerId, ni, true /* legacy */);
}
private static NetworkInfo getLegacyNetworkInfo(final NetworkAgentConfig config) {
// The subtype can be changed with (TODO) setLegacySubtype, but it starts
// with the type and an empty description.
- return new NetworkInfo(config.legacyType, config.legacyType, config.legacyTypeName, "");
+ final NetworkInfo ni = new NetworkInfo(config.legacyType, config.legacyType,
+ config.legacyTypeName, "");
+ ni.setIsAvailable(true);
+ return ni;
}
/**
@@ -310,15 +324,17 @@
@NonNull NetworkAgentConfig config, @Nullable NetworkProvider provider) {
this(looper, context, logTag, nc, lp, score, config,
provider == null ? NetworkProvider.ID_NONE : provider.getProviderId(),
- getLegacyNetworkInfo(config));
+ getLegacyNetworkInfo(config), false /* legacy */);
}
private NetworkAgent(Looper looper, Context context, String logTag, NetworkCapabilities nc,
LinkProperties lp, int score, NetworkAgentConfig config, int providerId,
- NetworkInfo ni) {
+ NetworkInfo ni, boolean legacy) {
mHandler = new NetworkAgentHandler(looper);
LOG_TAG = logTag;
mContext = context;
+ mIsLegacy = legacy;
+ mNetworkInfo = new NetworkInfo(ni);
this.providerId = providerId;
if (ni == null || nc == null || lp == null) {
throw new IllegalArgumentException();
@@ -483,15 +499,89 @@
* @param linkProperties the new LinkProperties.
*/
public void sendLinkProperties(@NonNull LinkProperties linkProperties) {
+ Objects.requireNonNull(linkProperties);
queueOrSendMessage(EVENT_NETWORK_PROPERTIES_CHANGED, new LinkProperties(linkProperties));
}
/**
+ * Inform ConnectivityService that this agent has now connected.
+ */
+ public void setConnected() {
+ if (mIsLegacy) {
+ throw new UnsupportedOperationException(
+ "Legacy agents can't call setConnected.");
+ }
+ mNetworkInfo.setDetailedState(NetworkInfo.DetailedState.CONNECTED, null, null);
+ queueOrSendMessage(EVENT_NETWORK_INFO_CHANGED, mNetworkInfo);
+ }
+
+ /**
+ * Unregister this network agent.
+ *
+ * This signals the network has disconnected and ends its lifecycle. After this is called,
+ * the network is torn down and this agent can no longer be used.
+ */
+ public void unregister() {
+ if (mIsLegacy) {
+ throw new UnsupportedOperationException(
+ "Legacy agents can't call unregister.");
+ }
+ mNetworkInfo.setDetailedState(NetworkInfo.DetailedState.DISCONNECTED, null, null);
+ queueOrSendMessage(EVENT_NETWORK_INFO_CHANGED, mNetworkInfo);
+ }
+
+ /**
+ * Change the legacy subtype of this network agent.
+ *
+ * This is only for backward compatibility and should not be used by non-legacy network agents,
+ * or agents that did not use to set a subtype. As such, only TYPE_MOBILE type agents can use
+ * this and others will be thrown an exception if they try.
+ *
+ * @deprecated this is for backward compatibility only.
+ * @param legacySubtype the legacy subtype.
+ */
+ @Deprecated
+ public void setLegacySubtype(final int legacySubtype, @NonNull final String legacySubtypeName) {
+ if (mIsLegacy) {
+ throw new UnsupportedOperationException("Legacy agents can't call setLegacySubtype.");
+ }
+ mNetworkInfo.setSubtype(legacySubtype, legacySubtypeName);
+ queueOrSendMessage(EVENT_NETWORK_INFO_CHANGED, mNetworkInfo);
+ }
+
+ /**
+ * Set the ExtraInfo of this network agent.
+ *
+ * This sets the ExtraInfo field inside the NetworkInfo returned by legacy public API and the
+ * broadcasts about the corresponding Network.
+ * This is only for backward compatibility and should not be used by non-legacy network agents,
+ * who will be thrown an exception if they try. The extra info should only be :
+ * <ul>
+ * <li>For cellular agents, the APN name.</li>
+ * <li>For ethernet agents, the interface name.</li>
+ * </ul>
+ *
+ * @deprecated this is for backward compatibility only.
+ * @param extraInfo the ExtraInfo.
+ */
+ @Deprecated
+ public void setLegacyExtraInfo(@Nullable final String extraInfo) {
+ if (mIsLegacy) {
+ throw new UnsupportedOperationException("Legacy agents can't call setLegacyExtraInfo.");
+ }
+ mNetworkInfo.setExtraInfo(extraInfo);
+ queueOrSendMessage(EVENT_NETWORK_INFO_CHANGED, mNetworkInfo);
+ }
+
+ /**
* Must be called by the agent when it has a new NetworkInfo object.
* @hide TODO: expose something better.
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
public void sendNetworkInfo(NetworkInfo networkInfo) {
+ if (!mIsLegacy) {
+ throw new UnsupportedOperationException("Only legacy agents can call sendNetworkInfo.");
+ }
queueOrSendMessage(EVENT_NETWORK_INFO_CHANGED, new NetworkInfo(networkInfo));
}
@@ -500,6 +590,7 @@
* @param networkCapabilities the new NetworkCapabilities.
*/
public void sendNetworkCapabilities(@NonNull NetworkCapabilities networkCapabilities) {
+ Objects.requireNonNull(networkCapabilities);
mBandwidthUpdatePending.set(false);
mLastBwRefreshTime = System.currentTimeMillis();
queueOrSendMessage(EVENT_NETWORK_CAPABILITIES_CHANGED,
diff --git a/core/java/android/net/NetworkCapabilities.java b/core/java/android/net/NetworkCapabilities.java
index ef4a9e5..873d6e914 100644
--- a/core/java/android/net/NetworkCapabilities.java
+++ b/core/java/android/net/NetworkCapabilities.java
@@ -830,6 +830,23 @@
* <p>This field keeps track of the UID of the app that created this network and is in charge of
* its lifecycle. This could be the UID of apps such as the Wifi network suggestor, the running
* VPN, or Carrier Service app managing a cellular data connection.
+ *
+ * <p>For NetworkCapability instances being sent from ConnectivityService, this value MUST be
+ * reset to Process.INVALID_UID unless all the following conditions are met:
+ *
+ * <ol>
+ * <li>The destination app is the network owner
+ * <li>The destination app has the ACCESS_FINE_LOCATION permission granted
+ * <li>The user's location toggle is on
+ * </ol>
+ *
+ * This is because the owner UID is location-sensitive. The apps that request a network could
+ * know where the device is if they can tell for sure the system has connected to the network
+ * they requested.
+ *
+ * <p>This is populated by the network agents and for the NetworkCapabilities instance sent by
+ * an app to the System Server, the value MUST be reset to Process.INVALID_UID by the system
+ * server.
*/
private int mOwnerUid = Process.INVALID_UID;
@@ -842,7 +859,16 @@
}
/**
- * Retrieves the UID of the owner app.
+ * Retrieves the UID of the app that owns this network.
+ *
+ * <p>For user privacy reasons, this field will only be populated if:
+ *
+ * <ol>
+ * <li>The calling app is the network owner
+ * <li>The calling app has the ACCESS_FINE_LOCATION permission granted
+ * <li>The user's location toggle is on
+ * </ol>
+ *
*/
public int getOwnerUid() {
return mOwnerUid;
@@ -880,8 +906,9 @@
* @param administratorUids the UIDs to be set as administrators of this Network.
* @hide
*/
+ @NonNull
@SystemApi
- public @NonNull NetworkCapabilities setAdministratorUids(
+ public NetworkCapabilities setAdministratorUids(
@NonNull final List<Integer> administratorUids) {
mAdministratorUids.clear();
mAdministratorUids.addAll(administratorUids);
diff --git a/core/java/android/net/VpnManager.java b/core/java/android/net/VpnManager.java
index f19ba0f..2041cfb 100644
--- a/core/java/android/net/VpnManager.java
+++ b/core/java/android/net/VpnManager.java
@@ -126,7 +126,11 @@
return getIntentForConfirmation();
}
- /** Delete the VPN profile configuration that was provisioned by the calling app */
+ /**
+ * Delete the VPN profile configuration that was provisioned by the calling app
+ *
+ * @throws SecurityException if this would violate user settings
+ */
public void deleteProvisionedVpnProfile() {
try {
mService.deleteVpnProfile(mContext.getOpPackageName());
diff --git a/core/java/android/os/BaseBundle.java b/core/java/android/os/BaseBundle.java
index e6ad917..c2f6794 100644
--- a/core/java/android/os/BaseBundle.java
+++ b/core/java/android/os/BaseBundle.java
@@ -365,12 +365,16 @@
}
/**
+ * This method returns true when the parcel is 'definitely' empty.
+ * That is, it may return false for an empty parcel. But will never return true for a non-empty
+ * one.
+ *
* @hide this should probably be the implementation of isEmpty(). To do that we
* need to ensure we always use the special empty parcel form when the bundle is
* empty. (This may already be the case, but to be safe we'll do this later when
* we aren't trying to stabilize.)
*/
- public boolean maybeIsEmpty() {
+ public boolean isDefinitelyEmpty() {
if (isParcelled()) {
return isEmptyParcel();
} else {
@@ -402,6 +406,9 @@
if (other == null) {
return false;
}
+ if (isDefinitelyEmpty() && other.isDefinitelyEmpty()) {
+ return true;
+ }
if (isParcelled() != other.isParcelled()) {
// Big kind-of here!
return false;
diff --git a/core/java/android/os/image/DynamicSystemClient.java b/core/java/android/os/image/DynamicSystemClient.java
index 5cb3361..50d8d80 100644
--- a/core/java/android/os/image/DynamicSystemClient.java
+++ b/core/java/android/os/image/DynamicSystemClient.java
@@ -96,9 +96,6 @@
private static final String TAG = "DynSystemClient";
- private static final long DEFAULT_USERDATA_SIZE = (10L << 30);
-
-
/** Listener for installation status updates. */
public interface OnStatusChangedListener {
/**
@@ -386,7 +383,7 @@
@SystemApi
@TestApi
public void start(@NonNull Uri systemUrl, @BytesLong long systemSize) {
- start(systemUrl, systemSize, DEFAULT_USERDATA_SIZE);
+ start(systemUrl, systemSize, 0 /* Use the default userdata size */);
}
/**
diff --git a/core/java/android/os/image/DynamicSystemManager.java b/core/java/android/os/image/DynamicSystemManager.java
index cbf531c..17851ad 100644
--- a/core/java/android/os/image/DynamicSystemManager.java
+++ b/core/java/android/os/image/DynamicSystemManager.java
@@ -19,6 +19,7 @@
import android.annotation.RequiresPermission;
import android.annotation.SystemService;
import android.content.Context;
+import android.gsi.AvbPublicKey;
import android.gsi.GsiProgress;
import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
@@ -85,6 +86,23 @@
throw new RuntimeException(e.toString());
}
}
+
+ /**
+ * Retrieve AVB public key from installing partition.
+ *
+ * @param dst Output the AVB public key.
+ * @return true on success, false if partition doesn't have a
+ * valid VBMeta block to retrieve the AVB key from.
+ */
+ @RequiresPermission(android.Manifest.permission.MANAGE_DYNAMIC_SYSTEM)
+ public boolean getAvbPublicKey(AvbPublicKey dst) {
+ try {
+ return mService.getAvbPublicKey(dst);
+ } catch (RemoteException e) {
+ throw new RuntimeException(e.toString());
+ }
+ }
+
/**
* Finish write and make device to boot into the it after reboot.
*
diff --git a/core/java/android/os/image/IDynamicSystemService.aidl b/core/java/android/os/image/IDynamicSystemService.aidl
index cc32f99..a1f9272 100644
--- a/core/java/android/os/image/IDynamicSystemService.aidl
+++ b/core/java/android/os/image/IDynamicSystemService.aidl
@@ -15,6 +15,7 @@
*/
package android.os.image;
+import android.gsi.AvbPublicKey;
import android.gsi.GsiProgress;
/** {@hide} */
@@ -108,4 +109,13 @@
* @return true on success, false otherwise.
*/
boolean submitFromAshmem(long bytes);
+
+ /**
+ * Retrieve AVB public key from installing partition.
+ *
+ * @param dst Output the AVB public key.
+ * @return true on success, false if partition doesn't have a
+ * valid VBMeta block to retrieve the AVB key from.
+ */
+ boolean getAvbPublicKey(out AvbPublicKey dst);
}
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 3842def..dcbbb70 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -9739,11 +9739,17 @@
private static final Validator BUGREPORT_IN_POWER_MENU_VALIDATOR = BOOLEAN_VALIDATOR;
/**
- * Whether ADB is enabled.
+ * Whether ADB over USB is enabled.
*/
public static final String ADB_ENABLED = "adb_enabled";
/**
+ * Whether ADB over Wifi is enabled.
+ * @hide
+ */
+ public static final String ADB_WIFI_ENABLED = "adb_wifi_enabled";
+
+ /**
* Whether Views are allowed to save their attribute data.
* @hide
*/
diff --git a/core/java/android/service/carrier/CarrierMessagingServiceWrapper.java b/core/java/android/service/carrier/CarrierMessagingServiceWrapper.java
index de90b94..2a809b1 100644
--- a/core/java/android/service/carrier/CarrierMessagingServiceWrapper.java
+++ b/core/java/android/service/carrier/CarrierMessagingServiceWrapper.java
@@ -18,7 +18,6 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.annotation.SystemApi;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@@ -47,7 +46,6 @@
* CarrierMessagingService.
* @hide
*/
-@SystemApi
public abstract class CarrierMessagingServiceWrapper {
// Populated by bindToCarrierMessagingService. bindToCarrierMessagingService must complete
// prior to calling disposeConnection so that mCarrierMessagingServiceConnection is initialized.
@@ -64,7 +62,6 @@
* @return true upon successfully binding to a carrier messaging service, false otherwise
* @hide
*/
- @SystemApi
public boolean bindToCarrierMessagingService(@NonNull Context context,
@NonNull String carrierPackageName) {
Preconditions.checkState(mCarrierMessagingServiceConnection == null);
@@ -82,7 +79,6 @@
* @param context the context
* @hide
*/
- @SystemApi
public void disposeConnection(@NonNull Context context) {
Preconditions.checkNotNull(mCarrierMessagingServiceConnection);
context.unbindService(mCarrierMessagingServiceConnection);
@@ -93,7 +89,6 @@
* Implemented by subclasses to use the carrier messaging service once it is ready.
* @hide
*/
- @SystemApi
public abstract void onServiceReady();
/**
@@ -117,7 +112,6 @@
* @param callback the callback to notify upon completion
* @hide
*/
- @SystemApi
public void filterSms(@NonNull MessagePdu pdu, @NonNull String format, int destPort,
int subId, @NonNull final CarrierMessagingCallbackWrapper callback) {
if (mICarrierMessagingService != null) {
@@ -142,7 +136,6 @@
* @param callback the callback to notify upon completion
* @hide
*/
- @SystemApi
public void sendTextSms(@NonNull String text, int subId, @NonNull String destAddress,
int sendSmsFlag, @NonNull final CarrierMessagingCallbackWrapper callback) {
if (mICarrierMessagingService != null) {
@@ -168,7 +161,6 @@
* @param callback the callback to notify upon completion
* @hide
*/
- @SystemApi
public void sendDataSms(@NonNull byte[] data, int subId, @NonNull String destAddress,
int destPort, int sendSmsFlag,
@NonNull final CarrierMessagingCallbackWrapper callback) {
@@ -194,7 +186,6 @@
* @param callback the callback to notify upon completion
* @hide
*/
- @SystemApi
public void sendMultipartTextSms(@NonNull List<String> parts, int subId,
@NonNull String destAddress, int sendSmsFlag,
@NonNull final CarrierMessagingCallbackWrapper callback) {
@@ -220,7 +211,6 @@
* @param callback the callback to notify upon completion
* @hide
*/
- @SystemApi
public void sendMms(@NonNull Uri pduUri, int subId, @NonNull Uri location,
@NonNull final CarrierMessagingCallbackWrapper callback) {
if (mICarrierMessagingService != null) {
@@ -244,7 +234,6 @@
* @param callback the callback to notify upon completion
* @hide
*/
- @SystemApi
public void downloadMms(@NonNull Uri pduUri, int subId, @NonNull Uri location,
@NonNull final CarrierMessagingCallbackWrapper callback) {
if (mICarrierMessagingService != null) {
@@ -276,7 +265,6 @@
* {@link CarrierMessagingServiceWrapper}.
* @hide
*/
- @SystemApi
public abstract static class CarrierMessagingCallbackWrapper {
/**
@@ -289,7 +277,6 @@
* {@see CarrierMessagingService#onReceiveTextSms}.
* @hide
*/
- @SystemApi
public void onFilterComplete(int result) {
}
@@ -304,7 +291,6 @@
* only if result is {@link CarrierMessagingService#SEND_STATUS_OK}.
* @hide
*/
- @SystemApi
public void onSendSmsComplete(int result, int messageRef) {
}
@@ -319,7 +305,6 @@
* {@link CarrierMessagingService#SEND_STATUS_OK}.
* @hide
*/
- @SystemApi
public void onSendMultipartSmsComplete(int result, @Nullable int[] messageRefs) {
}
@@ -334,7 +319,6 @@
* {@link CarrierMessagingService#SEND_STATUS_OK}.
* @hide
*/
- @SystemApi
public void onSendMmsComplete(int result, @Nullable byte[] sendConfPdu) {
}
@@ -346,7 +330,6 @@
* and {@link CarrierMessagingService#SEND_STATUS_ERROR}.
* @hide
*/
- @SystemApi
public void onDownloadMmsComplete(int result) {
}
diff --git a/core/java/android/telephony/TelephonyRegistryManager.java b/core/java/android/telephony/TelephonyRegistryManager.java
index a242294..ddc38bf 100644
--- a/core/java/android/telephony/TelephonyRegistryManager.java
+++ b/core/java/android/telephony/TelephonyRegistryManager.java
@@ -18,7 +18,6 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
-import android.annotation.SystemApi;
import android.annotation.TestApi;
import android.content.Context;
import android.os.Binder;
@@ -61,7 +60,6 @@
*
* @hide
*/
-@SystemApi
public class TelephonyRegistryManager {
private static final String TAG = "TelephonyRegistryManager";
@@ -250,7 +248,6 @@
* @param incomingNumber incoming phone number.
* @hide
*/
- @SystemApi
@TestApi
@RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
public void notifyCallStateChangedForAllSubscriptions(@CallState int state,
diff --git a/core/java/android/view/accessibility/OWNERS b/core/java/android/view/accessibility/OWNERS
index 265674a..c6f42f7 100644
--- a/core/java/android/view/accessibility/OWNERS
+++ b/core/java/android/view/accessibility/OWNERS
@@ -1,3 +1,4 @@
svetoslavganov@google.com
pweaver@google.com
rhedjao@google.com
+qasid@google.com
diff --git a/core/java/com/android/internal/app/IBatteryStats.aidl b/core/java/com/android/internal/app/IBatteryStats.aidl
index 15b1d75..9c8ab0c 100644
--- a/core/java/com/android/internal/app/IBatteryStats.aidl
+++ b/core/java/com/android/internal/app/IBatteryStats.aidl
@@ -133,7 +133,7 @@
void noteNetworkStatsEnabled();
void noteDeviceIdleMode(int mode, String activeReason, int activeUid);
void setBatteryState(int status, int health, int plugType, int level, int temp, int volt,
- int chargeUAh, int chargeFullUAh);
+ int chargeUAh, int chargeFullUAh, long chargeTimeToFullSeconds);
@UnsupportedAppUsage
long getAwakeTimeBattery();
long getAwakeTimePlugged();
diff --git a/core/java/com/android/internal/net/VpnProfile.java b/core/java/com/android/internal/net/VpnProfile.java
index bbae027..23b1ab5 100644
--- a/core/java/com/android/internal/net/VpnProfile.java
+++ b/core/java/com/android/internal/net/VpnProfile.java
@@ -18,6 +18,7 @@
import android.annotation.NonNull;
import android.compat.annotation.UnsupportedAppUsage;
+import android.net.Ikev2VpnProfile;
import android.net.ProxyInfo;
import android.os.Build;
import android.os.Parcel;
@@ -332,15 +333,38 @@
return builder.toString().getBytes(StandardCharsets.UTF_8);
}
+ /** Checks if this profile specifies a LegacyVpn type. */
+ public static boolean isLegacyType(int type) {
+ switch (type) {
+ case VpnProfile.TYPE_IKEV2_IPSEC_USER_PASS: // fall through
+ case VpnProfile.TYPE_IKEV2_IPSEC_RSA: // fall through
+ case VpnProfile.TYPE_IKEV2_IPSEC_PSK:
+ return false;
+ default:
+ return true;
+ }
+ }
+
+ private boolean isValidLockdownLegacyVpnProfile() {
+ return isLegacyType(type) && isServerAddressNumeric() && hasDns()
+ && areDnsAddressesNumeric();
+ }
+
+ private boolean isValidLockdownPlatformVpnProfile() {
+ return Ikev2VpnProfile.isValidVpnProfile(this);
+ }
+
/**
- * Tests if profile is valid for lockdown, which requires IPv4 address for both server and DNS.
- * Server hostnames would require using DNS before connection.
+ * Tests if profile is valid for lockdown.
+ *
+ * <p>For LegacyVpn profiles, this requires an IPv4 address for both the server and DNS.
+ *
+ * <p>For PlatformVpn profiles, this requires a server, an identifier and the relevant fields to
+ * be non-null.
*/
public boolean isValidLockdownProfile() {
return isTypeValidForLockdown()
- && isServerAddressNumeric()
- && hasDns()
- && areDnsAddressesNumeric();
+ && (isValidLockdownLegacyVpnProfile() || isValidLockdownPlatformVpnProfile());
}
/** Returns {@code true} if the VPN type is valid for lockdown. */
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 183c0fb..3d6f233 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -996,6 +996,8 @@
private int mMinLearnedBatteryCapacity = -1;
private int mMaxLearnedBatteryCapacity = -1;
+ private long mBatteryTimeToFullSeconds = -1;
+
private long[] mCpuFreqs;
@VisibleForTesting
@@ -12218,7 +12220,7 @@
@GuardedBy("this")
public void setBatteryStateLocked(final int status, final int health, final int plugType,
final int level, /* not final */ int temp, final int volt, final int chargeUAh,
- final int chargeFullUAh) {
+ final int chargeFullUAh, final long chargeTimeToFullSeconds) {
// Temperature is encoded without the signed bit, so clamp any negative temperatures to 0.
temp = Math.max(0, temp);
@@ -12421,6 +12423,8 @@
mMinLearnedBatteryCapacity = Math.min(mMinLearnedBatteryCapacity, chargeFullUAh);
}
mMaxLearnedBatteryCapacity = Math.max(mMaxLearnedBatteryCapacity, chargeFullUAh);
+
+ mBatteryTimeToFullSeconds = chargeTimeToFullSeconds;
}
public static boolean isOnBattery(int plugType, int status) {
@@ -12570,19 +12574,10 @@
// Not yet working.
return -1;
}
- /* Broken
- int curLevel = mCurrentBatteryLevel;
- int plugLevel = mDischargePlugLevel;
- if (plugLevel < 0 || curLevel < (plugLevel+1)) {
- return -1;
+ if (mBatteryTimeToFullSeconds >= 0) {
+ return mBatteryTimeToFullSeconds * (1000 * 1000); // s to us
}
- long duration = computeBatteryRealtime(curTime, STATS_SINCE_UNPLUGGED);
- if (duration < 1000*1000) {
- return -1;
- }
- long usPerLevel = duration/(curLevel-plugLevel);
- return usPerLevel * (100-curLevel);
- */
+ // Else use algorithmic approach
if (mChargeStepTracker.mNumStepDurations < 1) {
return -1;
}
@@ -12590,7 +12585,7 @@
if (msPerLevel <= 0) {
return -1;
}
- return (msPerLevel * (100-mCurrentBatteryLevel)) * 1000;
+ return (msPerLevel * (100 - mCurrentBatteryLevel)) * 1000;
}
/*@hide */
diff --git a/core/java/com/android/internal/os/KernelCpuUidBpfMapReader.java b/core/java/com/android/internal/os/KernelCpuUidBpfMapReader.java
new file mode 100644
index 0000000..26f81d9
--- /dev/null
+++ b/core/java/com/android/internal/os/KernelCpuUidBpfMapReader.java
@@ -0,0 +1,202 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.os;
+
+import android.os.StrictMode;
+import android.os.SystemClock;
+import android.util.Slog;
+import android.util.SparseArray;
+
+import java.util.concurrent.locks.ReentrantReadWriteLock;
+
+/**
+ * Reads cpu time bpf maps.
+ *
+ * It is implemented as singletons for each separate set of per-UID times. Get___Instance() method
+ * returns the corresponding reader instance. In order to prevent frequent GC, it reuses the same
+ * SparseArray to store data read from BPF maps.
+ *
+ * A KernelCpuUidBpfMapReader instance keeps an error counter. When the number of read errors within
+ * that instance accumulates to 5, this instance will reject all further read requests.
+ *
+ * Data fetched within last 500ms is considered fresh, since the reading lifecycle can take up to
+ * 25ms. KernelCpuUidBpfMapReader always tries to use cache if it is fresh and valid, but it can
+ * be disabled through a parameter.
+ *
+ * A KernelCpuUidBpfMapReader instance is thread-safe. It acquires a write lock when reading the bpf
+ * map, releases it right after, then acquires a read lock before returning a BpfMapIterator. Caller
+ * is responsible for closing BpfMapIterator (also auto-closable) after reading, otherwise deadlock
+ * will occur.
+ */
+public abstract class KernelCpuUidBpfMapReader {
+ private static final int ERROR_THRESHOLD = 5;
+ private static final long FRESHNESS_MS = 500L;
+
+ private static final KernelCpuUidBpfMapReader FREQ_TIME_READER =
+ new KernelCpuUidFreqTimeBpfMapReader();
+
+ private static final KernelCpuUidBpfMapReader ACTIVE_TIME_READER =
+ new KernelCpuUidActiveTimeBpfMapReader();
+
+ private static final KernelCpuUidBpfMapReader CLUSTER_TIME_READER =
+ new KernelCpuUidClusterTimeBpfMapReader();
+
+ static KernelCpuUidBpfMapReader getFreqTimeReaderInstance() {
+ return FREQ_TIME_READER;
+ }
+
+ static KernelCpuUidBpfMapReader getActiveTimeReaderInstance() {
+ return ACTIVE_TIME_READER;
+ }
+
+ static KernelCpuUidBpfMapReader getClusterTimeReaderInstance() {
+ return CLUSTER_TIME_READER;
+ }
+
+ final String mTag = this.getClass().getSimpleName();
+ private int mErrors = 0;
+ private boolean mTracking = false;
+ protected SparseArray<long[]> mData = new SparseArray<>();
+ private long mLastReadTime = 0;
+ protected final ReentrantReadWriteLock mLock = new ReentrantReadWriteLock();
+ protected final ReentrantReadWriteLock.ReadLock mReadLock = mLock.readLock();
+ protected final ReentrantReadWriteLock.WriteLock mWriteLock = mLock.writeLock();
+
+ public native boolean startTrackingBpfTimes();
+
+ protected abstract boolean readBpfData();
+
+ /**
+ * Returns an array of metadata used to inform the caller of 1) the size of array required by
+ * getNextUid and 2) how to interpret the raw data copied to that array.
+ */
+ public abstract long[] getDataDimensions();
+
+ public void removeUidsInRange(int startUid, int endUid) {
+ if (mErrors > ERROR_THRESHOLD) {
+ return;
+ }
+ mWriteLock.lock();
+ int firstIndex = mData.indexOfKey(startUid);
+ int lastIndex = mData.indexOfKey(endUid);
+ mData.removeAtRange(firstIndex, lastIndex - firstIndex + 1);
+ mWriteLock.unlock();
+ }
+
+ public BpfMapIterator open() {
+ return open(false);
+ }
+
+ public BpfMapIterator open(boolean ignoreCache) {
+ if (mErrors > ERROR_THRESHOLD) {
+ return null;
+ }
+ if (!mTracking && !startTrackingBpfTimes()) {
+ Slog.w(mTag, "Failed to start tracking");
+ mErrors++;
+ return null;
+ }
+ if (ignoreCache) {
+ mWriteLock.lock();
+ } else {
+ mReadLock.lock();
+ if (dataValid()) {
+ return new BpfMapIterator();
+ }
+ mReadLock.unlock();
+ mWriteLock.lock();
+ if (dataValid()) {
+ mReadLock.lock();
+ mWriteLock.unlock();
+ return new BpfMapIterator();
+ }
+ }
+ if (readBpfData()) {
+ mLastReadTime = SystemClock.elapsedRealtime();
+ mReadLock.lock();
+ mWriteLock.unlock();
+ return new BpfMapIterator();
+ }
+
+ mWriteLock.unlock();
+ mErrors++;
+ Slog.w(mTag, "Failed to read bpf times");
+ return null;
+ }
+
+ private boolean dataValid() {
+ return mData.size() > 0 && (SystemClock.elapsedRealtime() - mLastReadTime < FRESHNESS_MS);
+ }
+
+ public class BpfMapIterator implements AutoCloseable {
+ private int mPos;
+
+ public BpfMapIterator() {
+ };
+
+ public boolean getNextUid(long[] buf) {
+ if (mPos >= mData.size()) {
+ return false;
+ }
+ buf[0] = mData.keyAt(mPos);
+ System.arraycopy(mData.valueAt(mPos), 0, buf, 1, mData.valueAt(mPos).length);
+ mPos++;
+ return true;
+ }
+
+ public void close() {
+ mReadLock.unlock();
+ }
+ }
+
+ public static class KernelCpuUidFreqTimeBpfMapReader extends KernelCpuUidBpfMapReader {
+
+ private final native boolean removeUidRange(int startUid, int endUid);
+
+ @Override
+ protected final native boolean readBpfData();
+
+ @Override
+ public final native long[] getDataDimensions();
+
+ @Override
+ public void removeUidsInRange(int startUid, int endUid) {
+ mWriteLock.lock();
+ super.removeUidsInRange(startUid, endUid);
+ removeUidRange(startUid, endUid);
+ mWriteLock.unlock();
+ }
+ }
+
+ public static class KernelCpuUidActiveTimeBpfMapReader extends KernelCpuUidBpfMapReader {
+
+ @Override
+ protected final native boolean readBpfData();
+
+ @Override
+ public final native long[] getDataDimensions();
+ }
+
+ public static class KernelCpuUidClusterTimeBpfMapReader extends KernelCpuUidBpfMapReader {
+
+ @Override
+ protected final native boolean readBpfData();
+
+ @Override
+ public final native long[] getDataDimensions();
+ }
+}
diff --git a/core/java/com/android/internal/os/KernelCpuUidTimeReader.java b/core/java/com/android/internal/os/KernelCpuUidTimeReader.java
index e6d044f..34e75fe 100644
--- a/core/java/com/android/internal/os/KernelCpuUidTimeReader.java
+++ b/core/java/com/android/internal/os/KernelCpuUidTimeReader.java
@@ -28,6 +28,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.os.KernelCpuProcStringReader.ProcFileIterator;
+import com.android.internal.os.KernelCpuUidBpfMapReader.BpfMapIterator;
import java.io.BufferedReader;
import java.io.FileWriter;
@@ -57,6 +58,8 @@
final SparseArray<T> mLastTimes = new SparseArray<>();
final KernelCpuProcStringReader mReader;
final boolean mThrottle;
+ protected boolean mBpfTimesAvailable;
+ final KernelCpuUidBpfMapReader mBpfReader;
private long mMinTimeBetweenRead = DEFAULT_MIN_TIME_BETWEEN_READ;
private long mLastReadTimeMs = 0;
@@ -73,9 +76,15 @@
void onUidCpuTime(int uid, T time);
}
- KernelCpuUidTimeReader(KernelCpuProcStringReader reader, boolean throttle) {
+ KernelCpuUidTimeReader(KernelCpuProcStringReader reader, @Nullable KernelCpuUidBpfMapReader bpfReader, boolean throttle) {
mReader = reader;
mThrottle = throttle;
+ mBpfReader = bpfReader;
+ mBpfTimesAvailable = (mBpfReader != null);
+ }
+
+ KernelCpuUidTimeReader(KernelCpuProcStringReader reader, boolean throttle) {
+ this(reader, null, throttle);
}
/**
@@ -151,9 +160,13 @@
}
mLastTimes.put(startUid, null);
mLastTimes.put(endUid, null);
- final int firstIndex = mLastTimes.indexOfKey(startUid);
- final int lastIndex = mLastTimes.indexOfKey(endUid);
+ int firstIndex = mLastTimes.indexOfKey(startUid);
+ int lastIndex = mLastTimes.indexOfKey(endUid);
mLastTimes.removeAtRange(firstIndex, lastIndex - firstIndex + 1);
+
+ if (mBpfTimesAvailable) {
+ mBpfReader.removeUidsInRange(startUid, endUid);
+ }
}
/**
@@ -323,13 +336,13 @@
public KernelCpuUidFreqTimeReader(boolean throttle) {
this(UID_TIMES_PROC_FILE, KernelCpuProcStringReader.getFreqTimeReaderInstance(),
- throttle);
+ KernelCpuUidBpfMapReader.getFreqTimeReaderInstance(), throttle);
}
@VisibleForTesting
public KernelCpuUidFreqTimeReader(String procFile, KernelCpuProcStringReader reader,
- boolean throttle) {
- super(reader, throttle);
+ KernelCpuUidBpfMapReader bpfReader, boolean throttle) {
+ super(reader, bpfReader, throttle);
mProcFilePath = Paths.get(procFile);
}
@@ -370,19 +383,24 @@
if (!mAllUidTimesAvailable) {
return null;
}
- final int oldMask = StrictMode.allowThreadDiskReadsMask();
- try (BufferedReader reader = Files.newBufferedReader(mProcFilePath)) {
- if (readFreqs(reader.readLine()) == null) {
+ if (mBpfTimesAvailable) {
+ readFreqsThroughBpf();
+ }
+ if (mCpuFreqs == null) {
+ final int oldMask = StrictMode.allowThreadDiskReadsMask();
+ try (BufferedReader reader = Files.newBufferedReader(mProcFilePath)) {
+ if (readFreqs(reader.readLine()) == null) {
+ return null;
+ }
+ } catch (IOException e) {
+ if (++mErrors >= MAX_ERROR_COUNT) {
+ mAllUidTimesAvailable = false;
+ }
+ Slog.e(mTag, "Failed to read " + UID_TIMES_PROC_FILE + ": " + e);
return null;
+ } finally {
+ StrictMode.setThreadPolicyMask(oldMask);
}
- } catch (IOException e) {
- if (++mErrors >= MAX_ERROR_COUNT) {
- mAllUidTimesAvailable = false;
- }
- Slog.e(mTag, "Failed to read " + UID_TIMES_PROC_FILE + ": " + e);
- return null;
- } finally {
- StrictMode.setThreadPolicyMask(oldMask);
}
// Check if the freqs in the proc file correspond to per-cluster freqs.
final IntArray numClusterFreqs = extractClusterInfoFromProcFileFreqs();
@@ -402,6 +420,21 @@
return mCpuFreqs;
}
+ private long[] readFreqsThroughBpf() {
+ if (!mBpfTimesAvailable || mBpfReader == null) {
+ return null;
+ }
+ mCpuFreqs = mBpfReader.getDataDimensions();
+ if (mCpuFreqs == null) {
+ return null;
+ }
+ mFreqCount = mCpuFreqs.length;
+ mCurTimes = new long[mFreqCount];
+ mDeltaTimes = new long[mFreqCount];
+ mBuffer = new long[mFreqCount + 1];
+ return mCpuFreqs;
+ }
+
private long[] readFreqs(String line) {
if (line == null) {
return null;
@@ -422,8 +455,45 @@
return mCpuFreqs;
}
+ private void processUidDelta(@Nullable Callback<long[]> cb) {
+ final int uid = (int) mBuffer[0];
+ long[] lastTimes = mLastTimes.get(uid);
+ if (lastTimes == null) {
+ lastTimes = new long[mFreqCount];
+ mLastTimes.put(uid, lastTimes);
+ }
+ copyToCurTimes();
+ boolean notify = false;
+ boolean valid = true;
+ for (int i = 0; i < mFreqCount; i++) {
+ // Unit is 10ms.
+ mDeltaTimes[i] = mCurTimes[i] - lastTimes[i];
+ if (mDeltaTimes[i] < 0) {
+ Slog.e(mTag, "Negative delta from freq time proc: " + mDeltaTimes[i]);
+ valid = false;
+ }
+ notify |= mDeltaTimes[i] > 0;
+ }
+ if (notify && valid) {
+ System.arraycopy(mCurTimes, 0, lastTimes, 0, mFreqCount);
+ if (cb != null) {
+ cb.onUidCpuTime(uid, mDeltaTimes);
+ }
+ }
+ }
+
@Override
void readDeltaImpl(@Nullable Callback<long[]> cb) {
+ if (mBpfTimesAvailable) {
+ try (BpfMapIterator iter = mBpfReader.open(!mThrottle)) {
+ if (checkPrecondition(iter)) {
+ while (iter.getNextUid(mBuffer)) {
+ processUidDelta(cb);
+ }
+ return;
+ }
+ }
+ }
try (ProcFileIterator iter = mReader.open(!mThrottle)) {
if (!checkPrecondition(iter)) {
return;
@@ -434,36 +504,24 @@
Slog.wtf(mTag, "Invalid line: " + buf.toString());
continue;
}
- final int uid = (int) mBuffer[0];
- long[] lastTimes = mLastTimes.get(uid);
- if (lastTimes == null) {
- lastTimes = new long[mFreqCount];
- mLastTimes.put(uid, lastTimes);
- }
- copyToCurTimes();
- boolean notify = false;
- boolean valid = true;
- for (int i = 0; i < mFreqCount; i++) {
- // Unit is 10ms.
- mDeltaTimes[i] = mCurTimes[i] - lastTimes[i];
- if (mDeltaTimes[i] < 0) {
- Slog.e(mTag, "Negative delta from freq time proc: " + mDeltaTimes[i]);
- valid = false;
- }
- notify |= mDeltaTimes[i] > 0;
- }
- if (notify && valid) {
- System.arraycopy(mCurTimes, 0, lastTimes, 0, mFreqCount);
- if (cb != null) {
- cb.onUidCpuTime(uid, mDeltaTimes);
- }
- }
+ processUidDelta(cb);
}
}
}
@Override
void readAbsoluteImpl(Callback<long[]> cb) {
+ if (mBpfTimesAvailable) {
+ try (BpfMapIterator iter = mBpfReader.open(!mThrottle)) {
+ if (checkPrecondition(iter)) {
+ while (iter.getNextUid(mBuffer)) {
+ copyToCurTimes();
+ cb.onUidCpuTime((int) mBuffer[0], mCurTimes);
+ }
+ return;
+ }
+ }
+ }
try (ProcFileIterator iter = mReader.open(!mThrottle)) {
if (!checkPrecondition(iter)) {
return;
@@ -481,11 +539,24 @@
}
private void copyToCurTimes() {
+ long factor = mBpfTimesAvailable ? 1 : 10;
for (int i = 0; i < mFreqCount; i++) {
- mCurTimes[i] = mBuffer[i + 1] * 10;
+ mCurTimes[i] = mBuffer[i + 1] * factor;
}
}
+ private boolean checkPrecondition(BpfMapIterator iter) {
+ if (iter == null) {
+ mBpfTimesAvailable = false;
+ return false;
+ }
+ if (mCpuFreqs != null) {
+ return true;
+ }
+ mBpfTimesAvailable = (readFreqsThroughBpf() != null);
+ return mBpfTimesAvailable;
+ }
+
private boolean checkPrecondition(ProcFileIterator iter) {
if (iter == null || !iter.hasNextLine()) {
// Error logged in KernelCpuProcStringReader.
@@ -544,16 +615,43 @@
private long[] mBuffer;
public KernelCpuUidActiveTimeReader(boolean throttle) {
- super(KernelCpuProcStringReader.getActiveTimeReaderInstance(), throttle);
+ super(KernelCpuProcStringReader.getActiveTimeReaderInstance(),
+ KernelCpuUidBpfMapReader.getActiveTimeReaderInstance(), throttle);
}
@VisibleForTesting
- public KernelCpuUidActiveTimeReader(KernelCpuProcStringReader reader, boolean throttle) {
- super(reader, throttle);
+ public KernelCpuUidActiveTimeReader(KernelCpuProcStringReader reader, KernelCpuUidBpfMapReader bpfReader, boolean throttle) {
+ super(reader, bpfReader, throttle);
+ }
+
+ private void processUidDelta(@Nullable Callback<Long> cb) {
+ int uid = (int) mBuffer[0];
+ long cpuActiveTime = sumActiveTime(mBuffer, mBpfTimesAvailable ? 1 : 10);
+ if (cpuActiveTime > 0) {
+ long delta = cpuActiveTime - mLastTimes.get(uid, 0L);
+ if (delta > 0) {
+ mLastTimes.put(uid, cpuActiveTime);
+ if (cb != null) {
+ cb.onUidCpuTime(uid, delta);
+ }
+ } else if (delta < 0) {
+ Slog.e(mTag, "Negative delta from active time proc: " + delta);
+ }
+ }
}
@Override
void readDeltaImpl(@Nullable Callback<Long> cb) {
+ if (mBpfTimesAvailable) {
+ try (BpfMapIterator iter = mBpfReader.open(!mThrottle)) {
+ if (checkPrecondition(iter)) {
+ while (iter.getNextUid(mBuffer)) {
+ processUidDelta(cb);
+ }
+ return;
+ }
+ }
+ }
try (ProcFileIterator iter = mReader.open(!mThrottle)) {
if (!checkPrecondition(iter)) {
return;
@@ -564,25 +662,30 @@
Slog.wtf(mTag, "Invalid line: " + buf.toString());
continue;
}
- int uid = (int) mBuffer[0];
- long cpuActiveTime = sumActiveTime(mBuffer);
- if (cpuActiveTime > 0) {
- long delta = cpuActiveTime - mLastTimes.get(uid, 0L);
- if (delta > 0) {
- mLastTimes.put(uid, cpuActiveTime);
- if (cb != null) {
- cb.onUidCpuTime(uid, delta);
- }
- } else if (delta < 0) {
- Slog.e(mTag, "Negative delta from active time proc: " + delta);
- }
- }
+ processUidDelta(cb);
}
}
}
+ private void processUidAbsolute(@Nullable Callback<Long> cb) {
+ long cpuActiveTime = sumActiveTime(mBuffer, mBpfTimesAvailable ? 1 : 10);
+ if (cpuActiveTime > 0) {
+ cb.onUidCpuTime((int) mBuffer[0], cpuActiveTime);
+ }
+ }
+
@Override
void readAbsoluteImpl(Callback<Long> cb) {
+ if (mBpfTimesAvailable) {
+ try (BpfMapIterator iter = mBpfReader.open(!mThrottle)) {
+ if (checkPrecondition(iter)) {
+ while (iter.getNextUid(mBuffer)) {
+ processUidAbsolute(cb);
+ }
+ return;
+ }
+ }
+ }
try (ProcFileIterator iter = mReader.open(!mThrottle)) {
if (!checkPrecondition(iter)) {
return;
@@ -593,23 +696,38 @@
Slog.wtf(mTag, "Invalid line: " + buf.toString());
continue;
}
- long cpuActiveTime = sumActiveTime(mBuffer);
- if (cpuActiveTime > 0) {
- cb.onUidCpuTime((int) mBuffer[0], cpuActiveTime);
- }
+ processUidAbsolute(cb);
}
}
}
- private static long sumActiveTime(long[] times) {
+ private static long sumActiveTime(long[] times, double factor) {
// UID is stored at times[0].
double sum = 0;
for (int i = 1; i < times.length; i++) {
- sum += (double) times[i] * 10 / i; // Unit is 10ms.
+ sum += (double) times[i] * factor / i; // Unit is 10ms.
}
return (long) sum;
}
+ private boolean checkPrecondition(BpfMapIterator iter) {
+ if (iter == null) {
+ mBpfTimesAvailable = false;
+ return false;
+ }
+ if (mCores > 0) {
+ return true;
+ }
+ long[] cores = mBpfReader.getDataDimensions();
+ if (cores == null || cores.length < 1) {
+ mBpfTimesAvailable = false;
+ return false;
+ }
+ mCores = (int) cores[0];
+ mBuffer = new long[mCores + 1];
+ return true;
+ }
+
private boolean checkPrecondition(ProcFileIterator iter) {
if (iter == null || !iter.hasNextLine()) {
// Error logged in KernelCpuProcStringReader.
@@ -664,16 +782,54 @@
private long[] mDeltaTime;
public KernelCpuUidClusterTimeReader(boolean throttle) {
- super(KernelCpuProcStringReader.getClusterTimeReaderInstance(), throttle);
+ super(KernelCpuProcStringReader.getClusterTimeReaderInstance(),
+ KernelCpuUidBpfMapReader.getClusterTimeReaderInstance(), throttle);
}
@VisibleForTesting
- public KernelCpuUidClusterTimeReader(KernelCpuProcStringReader reader, boolean throttle) {
- super(reader, throttle);
+ public KernelCpuUidClusterTimeReader(KernelCpuProcStringReader reader,
+ KernelCpuUidBpfMapReader bpfReader, boolean throttle) {
+ super(reader, bpfReader, throttle);
+ }
+
+ void processUidDelta(@Nullable Callback<long[]> cb) {
+ int uid = (int) mBuffer[0];
+ long[] lastTimes = mLastTimes.get(uid);
+ if (lastTimes == null) {
+ lastTimes = new long[mNumClusters];
+ mLastTimes.put(uid, lastTimes);
+ }
+ sumClusterTime();
+ boolean valid = true;
+ boolean notify = false;
+ for (int i = 0; i < mNumClusters; i++) {
+ mDeltaTime[i] = mCurTime[i] - lastTimes[i];
+ if (mDeltaTime[i] < 0) {
+ Slog.e(mTag, "Negative delta from cluster time proc: " + mDeltaTime[i]);
+ valid = false;
+ }
+ notify |= mDeltaTime[i] > 0;
+ }
+ if (notify && valid) {
+ System.arraycopy(mCurTime, 0, lastTimes, 0, mNumClusters);
+ if (cb != null) {
+ cb.onUidCpuTime(uid, mDeltaTime);
+ }
+ }
}
@Override
void readDeltaImpl(@Nullable Callback<long[]> cb) {
+ if (mBpfTimesAvailable) {
+ try (BpfMapIterator iter = mBpfReader.open(!mThrottle)) {
+ if (checkPrecondition(iter)) {
+ while (iter.getNextUid(mBuffer)) {
+ processUidDelta(cb);
+ }
+ return;
+ }
+ }
+ }
try (ProcFileIterator iter = mReader.open(!mThrottle)) {
if (!checkPrecondition(iter)) {
return;
@@ -684,35 +840,24 @@
Slog.wtf(mTag, "Invalid line: " + buf.toString());
continue;
}
- int uid = (int) mBuffer[0];
- long[] lastTimes = mLastTimes.get(uid);
- if (lastTimes == null) {
- lastTimes = new long[mNumClusters];
- mLastTimes.put(uid, lastTimes);
- }
- sumClusterTime();
- boolean valid = true;
- boolean notify = false;
- for (int i = 0; i < mNumClusters; i++) {
- mDeltaTime[i] = mCurTime[i] - lastTimes[i];
- if (mDeltaTime[i] < 0) {
- Slog.e(mTag, "Negative delta from cluster time proc: " + mDeltaTime[i]);
- valid = false;
- }
- notify |= mDeltaTime[i] > 0;
- }
- if (notify && valid) {
- System.arraycopy(mCurTime, 0, lastTimes, 0, mNumClusters);
- if (cb != null) {
- cb.onUidCpuTime(uid, mDeltaTime);
- }
- }
+ processUidDelta(cb);
}
}
}
@Override
void readAbsoluteImpl(Callback<long[]> cb) {
+ if (mBpfTimesAvailable) {
+ try (BpfMapIterator iter = mBpfReader.open(!mThrottle)) {
+ if (checkPrecondition(iter)) {
+ while (iter.getNextUid(mBuffer)) {
+ sumClusterTime();
+ cb.onUidCpuTime((int) mBuffer[0], mCurTime);
+ }
+ return;
+ }
+ }
+ }
try (ProcFileIterator iter = mReader.open(!mThrottle)) {
if (!checkPrecondition(iter)) {
return;
@@ -730,17 +875,45 @@
}
private void sumClusterTime() {
+ double factor = mBpfTimesAvailable ? 1 : 10;
// UID is stored at mBuffer[0].
int core = 1;
for (int i = 0; i < mNumClusters; i++) {
double sum = 0;
for (int j = 1; j <= mCoresOnClusters[i]; j++) {
- sum += (double) mBuffer[core++] * 10 / j; // Unit is 10ms.
+ sum += (double) mBuffer[core++] * factor / j; // Unit is 10ms.
}
mCurTime[i] = (long) sum;
}
}
+ private boolean checkPrecondition(BpfMapIterator iter) {
+ if (iter == null) {
+ mBpfTimesAvailable = false;
+ return false;
+ }
+ if (mNumClusters > 0) {
+ return true;
+ }
+ long[] coresOnClusters = mBpfReader.getDataDimensions();
+ if (coresOnClusters == null || coresOnClusters.length < 1) {
+ mBpfTimesAvailable = false;
+ return false;
+ }
+ mNumClusters = coresOnClusters.length;
+ mCoresOnClusters = new int[mNumClusters];
+ int cores = 0;
+ for (int i = 0; i < mNumClusters; i++) {
+ mCoresOnClusters[i] = (int) coresOnClusters[i];
+ cores += mCoresOnClusters[i];
+ }
+ mNumCores = cores;
+ mBuffer = new long[cores + 1];
+ mCurTime = new long[mNumClusters];
+ mDeltaTime = new long[mNumClusters];
+ return true;
+ }
+
private boolean checkPrecondition(ProcFileIterator iter) {
if (iter == null || !iter.hasNextLine()) {
// Error logged in KernelCpuProcStringReader.
diff --git a/core/java/com/android/internal/os/KernelSingleUidTimeReader.java b/core/java/com/android/internal/os/KernelSingleUidTimeReader.java
index 3c43a11..fc0ce6f 100644
--- a/core/java/com/android/internal/os/KernelSingleUidTimeReader.java
+++ b/core/java/com/android/internal/os/KernelSingleUidTimeReader.java
@@ -53,6 +53,8 @@
private int mReadErrorCounter;
@GuardedBy("this")
private boolean mSingleUidCpuTimesAvailable = true;
+ @GuardedBy("this")
+ private boolean mBpfTimesAvailable = true;
// We use the freq count obtained from /proc/uid_time_in_state to decide how many longs
// to read from each /proc/uid/<uid>/time_in_state. On the first read, verify if this is
// correct and if not, set {@link #mSingleUidCpuTimesAvailable} to false. This flag will
@@ -62,6 +64,8 @@
private final Injector mInjector;
+ private static final native boolean canReadBpfTimes();
+
KernelSingleUidTimeReader(int cpuFreqsCount) {
this(cpuFreqsCount, new Injector());
}
@@ -83,6 +87,18 @@
if (!mSingleUidCpuTimesAvailable) {
return null;
}
+ if (mBpfTimesAvailable) {
+ final long[] cpuTimesMs = mInjector.readBpfData(uid);
+ if (cpuTimesMs.length == 0) {
+ mBpfTimesAvailable = false;
+ } else if (!mCpuFreqsCountVerified && cpuTimesMs.length != mCpuFreqsCount) {
+ mSingleUidCpuTimesAvailable = false;
+ return null;
+ } else {
+ mCpuFreqsCountVerified = true;
+ return computeDelta(uid, cpuTimesMs);
+ }
+ }
// Read total cpu times from the proc file.
final String procFile = new StringBuilder(PROC_FILE_DIR)
.append(uid)
@@ -230,6 +246,8 @@
public byte[] readData(String procFile) throws IOException {
return Files.readAllBytes(Paths.get(procFile));
}
+
+ public native long[] readBpfData(int uid);
}
@VisibleForTesting
diff --git a/core/java/com/android/internal/os/PowerProfile.java b/core/java/com/android/internal/os/PowerProfile.java
index d3fe582..9d0cb01 100644
--- a/core/java/com/android/internal/os/PowerProfile.java
+++ b/core/java/com/android/internal/os/PowerProfile.java
@@ -393,6 +393,9 @@
}
public int getNumCoresInCpuCluster(int cluster) {
+ if (cluster < 0 || cluster >= mCpuClusters.length) {
+ return 0; // index out of bound
+ }
return mCpuClusters[cluster].numCpus;
}
diff --git a/core/java/com/android/internal/os/RuntimeInit.java b/core/java/com/android/internal/os/RuntimeInit.java
index 16c0b56..13d0c5c 100644
--- a/core/java/com/android/internal/os/RuntimeInit.java
+++ b/core/java/com/android/internal/os/RuntimeInit.java
@@ -19,8 +19,6 @@
import android.app.ActivityManager;
import android.app.ActivityThread;
import android.app.ApplicationErrorReport;
-import android.compat.annotation.ChangeId;
-import android.compat.annotation.EnabledAfter;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.type.DefaultMimeMapFactory;
import android.os.Build;
@@ -36,7 +34,6 @@
import com.android.internal.logging.AndroidConfig;
import com.android.server.NetworkManagementSocketTagger;
-import dalvik.annotation.compat.VersionCodes;
import dalvik.system.RuntimeHooks;
import dalvik.system.ThreadPrioritySetter;
import dalvik.system.VMRuntime;
@@ -67,18 +64,8 @@
private static volatile boolean mCrashing = false;
- /**
- * Native heap allocations will now have a non-zero tag in the most significant byte.
- * See
- * <a href="https://source.android.com/devices/tech/debug/tagged-pointers">https://source.android.com/devices/tech/debug/tagged-pointers</a>.
- */
- @ChangeId
- @EnabledAfter(targetSdkVersion = VersionCodes.Q)
- private static final long NATIVE_HEAP_POINTER_TAGGING = 135754954; // This is a bug id.
-
private static final native void nativeFinishInit();
private static final native void nativeSetExitWithoutCleanup(boolean exitWithoutCleanup);
- private static native void nativeDisableHeapPointerTagging();
private static int Clog_e(String tag, String msg, Throwable tr) {
return Log.printlns(Log.LOG_ID_CRASH, Log.ERROR, tag, msg, tr);
@@ -411,20 +398,6 @@
if (DEBUG) Slog.d(TAG, "Leaving RuntimeInit!");
}
- private static void maybeDisableHeapPointerTagging(long[] disabledCompatChanges) {
- // Heap tagging needs to be disabled before any additional threads are created, but the
- // AppCompat framework is not initialized enough at this point.
- // Check if the change is enabled manually.
- if (disabledCompatChanges != null) {
- for (int i = 0; i < disabledCompatChanges.length; i++) {
- if (disabledCompatChanges[i] == NATIVE_HEAP_POINTER_TAGGING) {
- nativeDisableHeapPointerTagging();
- break;
- }
- }
- }
- }
-
protected static Runnable applicationInit(int targetSdkVersion, long[] disabledCompatChanges,
String[] argv, ClassLoader classLoader) {
// If the application calls System.exit(), terminate the process
@@ -437,8 +410,6 @@
VMRuntime.getRuntime().setTargetSdkVersion(targetSdkVersion);
VMRuntime.getRuntime().setDisabledCompatChanges(disabledCompatChanges);
- maybeDisableHeapPointerTagging(disabledCompatChanges);
-
final Arguments args = new Arguments(argv);
// The end of of the RuntimeInit event (see #zygoteInit).
diff --git a/core/java/com/android/internal/os/Zygote.java b/core/java/com/android/internal/os/Zygote.java
index 72eb32a..5f196a0 100644
--- a/core/java/com/android/internal/os/Zygote.java
+++ b/core/java/com/android/internal/os/Zygote.java
@@ -121,6 +121,25 @@
*/
public static final int DISABLE_TEST_API_ENFORCEMENT_POLICY = 1 << 18;
+ public static final int MEMORY_TAG_LEVEL_MASK = (1 << 19) | (1 << 20);
+ /**
+ * Enable pointer tagging in this process.
+ * Tags are checked during memory deallocation, but not on access.
+ * TBI stands for Top-Byte-Ignore, an ARM CPU feature.
+ * {@link https://developer.arm.com/docs/den0024/latest/the-memory-management-unit/translation-table-configuration/virtual-address-tagging}
+ */
+ public static final int MEMORY_TAG_LEVEL_TBI = 1 << 19;
+
+ /**
+ * Enable asynchronous memory tag checks in this process.
+ */
+ public static final int MEMORY_TAG_LEVEL_ASYNC = 2 << 19;
+
+ /**
+ * Enable synchronous memory tag checks in this process.
+ */
+ public static final int MEMORY_TAG_LEVEL_SYNC = 3 << 19;
+
/** No external storage should be mounted. */
public static final int MOUNT_EXTERNAL_NONE = IVold.REMOUNT_MODE_NONE;
/** Default external storage should be mounted. */
diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java
index decc92c..300f71a 100644
--- a/core/java/com/android/internal/os/ZygoteInit.java
+++ b/core/java/com/android/internal/os/ZygoteInit.java
@@ -781,6 +781,10 @@
Zygote.applyDebuggerSystemProperty(parsedArgs);
Zygote.applyInvokeWithSystemProperty(parsedArgs);
+ /* Enable pointer tagging in the system server unconditionally. Hardware support for
+ * this is present in all ARMv8 CPUs; this flag has no effect on other platforms. */
+ parsedArgs.mRuntimeFlags |= Zygote.MEMORY_TAG_LEVEL_TBI;
+
if (shouldProfileSystemServer()) {
parsedArgs.mRuntimeFlags |= Zygote.PROFILE_SYSTEM_SERVER;
}
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index 43df2e4..872f26d1 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -207,6 +207,8 @@
"com_android_internal_os_AtomicDirectory.cpp",
"com_android_internal_os_ClassLoaderFactory.cpp",
"com_android_internal_os_FuseAppLoop.cpp",
+ "com_android_internal_os_KernelCpuUidBpfMapReader.cpp",
+ "com_android_internal_os_KernelSingleUidTimeReader.cpp",
"com_android_internal_os_Zygote.cpp",
"com_android_internal_os_ZygoteInit.cpp",
"com_android_internal_util_VirtualRefBasePtr.cpp",
@@ -303,6 +305,7 @@
"libdl",
"libdl_android",
"libstatslog",
+ "libtimeinstate",
"server_configurable_flags",
],
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index 5b80af5..a2a6716 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -230,6 +230,8 @@
extern int register_com_android_internal_os_AtomicDirectory(JNIEnv *env);
extern int register_com_android_internal_os_ClassLoaderFactory(JNIEnv* env);
extern int register_com_android_internal_os_FuseAppLoop(JNIEnv* env);
+extern int register_com_android_internal_os_KernelCpuUidBpfMapReader(JNIEnv *env);
+extern int register_com_android_internal_os_KernelSingleUidTimeReader(JNIEnv *env);
extern int register_com_android_internal_os_Zygote(JNIEnv *env);
extern int register_com_android_internal_os_ZygoteInit(JNIEnv *env);
extern int register_com_android_internal_util_VirtualRefBasePtr(JNIEnv *env);
@@ -282,14 +284,6 @@
gCurRuntime->setExitWithoutCleanup(exitWithoutCleanup);
}
-static void com_android_internal_os_RuntimeInit_nativeDisableHeapPointerTagging(
- JNIEnv* env, jobject clazz) {
- HeapTaggingLevel tag_level = M_HEAP_TAGGING_LEVEL_NONE;
- if (!android_mallopt(M_SET_HEAP_TAGGING_LEVEL, &tag_level, sizeof(tag_level))) {
- ALOGE("ERROR: could not disable heap pointer tagging\n");
- }
-}
-
/*
* JNI registration.
*/
@@ -301,8 +295,6 @@
(void*)com_android_internal_os_RuntimeInit_nativeFinishInit},
{"nativeSetExitWithoutCleanup", "(Z)V",
(void*)com_android_internal_os_RuntimeInit_nativeSetExitWithoutCleanup},
- {"nativeDisableHeapPointerTagging", "()V",
- (void*)com_android_internal_os_RuntimeInit_nativeDisableHeapPointerTagging},
};
return jniRegisterNativeMethods(env, "com/android/internal/os/RuntimeInit",
methods, NELEM(methods));
@@ -1650,6 +1642,8 @@
REG_JNI(register_com_android_internal_content_NativeLibraryHelper),
REG_JNI(register_com_android_internal_os_AtomicDirectory),
REG_JNI(register_com_android_internal_os_FuseAppLoop),
+ REG_JNI(register_com_android_internal_os_KernelCpuUidBpfMapReader),
+ REG_JNI(register_com_android_internal_os_KernelSingleUidTimeReader),
};
/*
diff --git a/core/jni/android_os_HwParcel.cpp b/core/jni/android_os_HwParcel.cpp
index 151dbfce..a88f891 100644
--- a/core/jni/android_os_HwParcel.cpp
+++ b/core/jni/android_os_HwParcel.cpp
@@ -292,19 +292,11 @@
return;
}
- const jchar *interfaceName = env->GetStringCritical(interfaceNameObj, NULL);
+ const char *interfaceName = env->GetStringUTFChars(interfaceNameObj, NULL);
if (interfaceName) {
- String8 interfaceNameCopy = String8(String16(
- reinterpret_cast<const char16_t *>(interfaceName),
- env->GetStringLength(interfaceNameObj)));
-
- env->ReleaseStringCritical(interfaceNameObj, interfaceName);
- interfaceName = NULL;
-
hardware::Parcel *parcel =
JHwParcel::GetNativeContext(env, thiz)->getParcel();
-
- bool valid = parcel->enforceInterface(interfaceNameCopy.string());
+ bool valid = parcel->enforceInterface(interfaceName);
if (!valid) {
jniThrowException(
@@ -312,6 +304,7 @@
"java/lang/SecurityException",
"HWBinder invocation to an incorrect interface");
}
+ env->ReleaseStringUTFChars(interfaceNameObj, interfaceName);
}
}
diff --git a/core/jni/android_os_Parcel.cpp b/core/jni/android_os_Parcel.cpp
index d80c071..d723ecc 100644
--- a/core/jni/android_os_Parcel.cpp
+++ b/core/jni/android_os_Parcel.cpp
@@ -625,8 +625,8 @@
IPCThreadState* threadState = IPCThreadState::self();
const int32_t oldPolicy = threadState->getStrictModePolicy();
const bool isValid = parcel->enforceInterface(
- String16(reinterpret_cast<const char16_t*>(str),
- env->GetStringLength(name)),
+ reinterpret_cast<const char16_t*>(str),
+ env->GetStringLength(name),
threadState);
env->ReleaseStringCritical(name, str);
if (isValid) {
diff --git a/core/jni/com_android_internal_os_KernelCpuUidBpfMapReader.cpp b/core/jni/com_android_internal_os_KernelCpuUidBpfMapReader.cpp
new file mode 100644
index 0000000..7c68de5
--- /dev/null
+++ b/core/jni/com_android_internal_os_KernelCpuUidBpfMapReader.cpp
@@ -0,0 +1,217 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "core_jni_helpers.h"
+
+#include <sys/sysinfo.h>
+
+#include <android-base/stringprintf.h>
+#include <cputimeinstate.h>
+
+namespace android {
+
+static constexpr uint64_t NSEC_PER_MSEC = 1000000;
+
+static struct {
+ jclass clazz;
+ jmethodID put;
+ jmethodID get;
+} gSparseArrayClassInfo;
+
+static jfieldID gmData;
+
+static jlongArray getUidArray(JNIEnv *env, jobject sparseAr, uint32_t uid, jsize sz) {
+ jlongArray ar = (jlongArray)env->CallObjectMethod(sparseAr, gSparseArrayClassInfo.get, uid);
+ if (!ar) {
+ ar = env->NewLongArray(sz);
+ if (ar == NULL) return ar;
+ env->CallVoidMethod(sparseAr, gSparseArrayClassInfo.put, uid, ar);
+ }
+ return ar;
+}
+
+static void copy2DVecToArray(JNIEnv *env, jlongArray ar, std::vector<std::vector<uint64_t>> &vec) {
+ jsize start = 0;
+ for (auto &subVec : vec) {
+ for (uint32_t i = 0; i < subVec.size(); ++i) subVec[i] /= NSEC_PER_MSEC;
+ env->SetLongArrayRegion(ar, start, subVec.size(),
+ reinterpret_cast<const jlong *>(subVec.data()));
+ start += subVec.size();
+ }
+}
+
+static jboolean KernelCpuUidFreqTimeBpfMapReader_removeUidRange(JNIEnv *env, jclass, jint startUid,
+ jint endUid) {
+ for (uint32_t uid = startUid; uid <= endUid; ++uid) {
+ if (!android::bpf::clearUidTimes(uid)) return false;
+ }
+ return true;
+}
+
+static jboolean KernelCpuUidFreqTimeBpfMapReader_readBpfData(JNIEnv *env, jobject thiz) {
+ static uint64_t lastUpdate = 0;
+ uint64_t newLastUpdate = lastUpdate;
+ auto sparseAr = env->GetObjectField(thiz, gmData);
+ if (sparseAr == NULL) return false;
+ auto data = android::bpf::getUidsUpdatedCpuFreqTimes(&newLastUpdate);
+ if (!data.has_value()) return false;
+
+ jsize s = 0;
+ for (auto &[uid, times] : *data) {
+ if (s == 0) {
+ for (const auto &subVec : times) s += subVec.size();
+ }
+ jlongArray ar = getUidArray(env, sparseAr, uid, s);
+ if (ar == NULL) return false;
+ copy2DVecToArray(env, ar, times);
+ }
+ lastUpdate = newLastUpdate;
+ return true;
+}
+
+static jlongArray KernelCpuUidFreqTimeBpfMapReader_getDataDimensions(JNIEnv *env, jobject) {
+ auto freqs = android::bpf::getCpuFreqs();
+ if (!freqs) return NULL;
+
+ std::vector<uint64_t> allFreqs;
+ for (const auto &vec : *freqs) std::copy(vec.begin(), vec.end(), std::back_inserter(allFreqs));
+
+ auto ar = env->NewLongArray(allFreqs.size());
+ if (ar != NULL) {
+ env->SetLongArrayRegion(ar, 0, allFreqs.size(),
+ reinterpret_cast<const jlong *>(allFreqs.data()));
+ }
+ return ar;
+}
+
+static const JNINativeMethod gFreqTimeMethods[] = {
+ {"removeUidRange", "(II)Z", (void *)KernelCpuUidFreqTimeBpfMapReader_removeUidRange},
+ {"readBpfData", "()Z", (void *)KernelCpuUidFreqTimeBpfMapReader_readBpfData},
+ {"getDataDimensions", "()[J", (void *)KernelCpuUidFreqTimeBpfMapReader_getDataDimensions},
+};
+
+static jboolean KernelCpuUidActiveTimeBpfMapReader_readBpfData(JNIEnv *env, jobject thiz) {
+ static uint64_t lastUpdate = 0;
+ uint64_t newLastUpdate = lastUpdate;
+ auto sparseAr = env->GetObjectField(thiz, gmData);
+ if (sparseAr == NULL) return false;
+ auto data = android::bpf::getUidsUpdatedConcurrentTimes(&newLastUpdate);
+ if (!data.has_value()) return false;
+
+ for (auto &[uid, times] : *data) {
+ // TODO: revise calling code so we can divide by NSEC_PER_MSEC here instead
+ for (auto &time : times.active) time /= NSEC_PER_MSEC;
+ jlongArray ar = getUidArray(env, sparseAr, uid, times.active.size());
+ if (ar == NULL) return false;
+ env->SetLongArrayRegion(ar, 0, times.active.size(),
+ reinterpret_cast<const jlong *>(times.active.data()));
+ }
+ lastUpdate = newLastUpdate;
+ return true;
+}
+
+static jlongArray KernelCpuUidActiveTimeBpfMapReader_getDataDimensions(JNIEnv *env, jobject) {
+ jlong nCpus = get_nprocs_conf();
+
+ auto ar = env->NewLongArray(1);
+ if (ar != NULL) env->SetLongArrayRegion(ar, 0, 1, &nCpus);
+ return ar;
+}
+
+static const JNINativeMethod gActiveTimeMethods[] = {
+ {"readBpfData", "()Z", (void *)KernelCpuUidActiveTimeBpfMapReader_readBpfData},
+ {"getDataDimensions", "()[J", (void *)KernelCpuUidActiveTimeBpfMapReader_getDataDimensions},
+};
+
+static jboolean KernelCpuUidClusterTimeBpfMapReader_readBpfData(JNIEnv *env, jobject thiz) {
+ static uint64_t lastUpdate = 0;
+ uint64_t newLastUpdate = lastUpdate;
+ auto sparseAr = env->GetObjectField(thiz, gmData);
+ if (sparseAr == NULL) return false;
+ auto data = android::bpf::getUidsUpdatedConcurrentTimes(&newLastUpdate);
+ if (!data.has_value()) return false;
+
+ jsize s = 0;
+ for (auto &[uid, times] : *data) {
+ if (s == 0) {
+ for (const auto &subVec : times.policy) s += subVec.size();
+ }
+ jlongArray ar = getUidArray(env, sparseAr, uid, s);
+ if (ar == NULL) return false;
+ copy2DVecToArray(env, ar, times.policy);
+ }
+ lastUpdate = newLastUpdate;
+ return true;
+}
+
+static jlongArray KernelCpuUidClusterTimeBpfMapReader_getDataDimensions(JNIEnv *env, jobject) {
+ auto times = android::bpf::getUidConcurrentTimes(0);
+ if (!times.has_value()) return NULL;
+
+ std::vector<jlong> clusterCores;
+ for (const auto &vec : times->policy) clusterCores.push_back(vec.size());
+ auto ar = env->NewLongArray(clusterCores.size());
+ if (ar != NULL) env->SetLongArrayRegion(ar, 0, clusterCores.size(), clusterCores.data());
+ return ar;
+}
+
+static const JNINativeMethod gClusterTimeMethods[] = {
+ {"readBpfData", "()Z", (void *)KernelCpuUidClusterTimeBpfMapReader_readBpfData},
+ {"getDataDimensions", "()[J",
+ (void *)KernelCpuUidClusterTimeBpfMapReader_getDataDimensions},
+};
+
+struct readerMethods {
+ const char *name;
+ const JNINativeMethod *methods;
+ int numMethods;
+};
+
+static const readerMethods gAllMethods[] = {
+ {"KernelCpuUidFreqTimeBpfMapReader", gFreqTimeMethods, NELEM(gFreqTimeMethods)},
+ {"KernelCpuUidActiveTimeBpfMapReader", gActiveTimeMethods, NELEM(gActiveTimeMethods)},
+ {"KernelCpuUidClusterTimeBpfMapReader", gClusterTimeMethods, NELEM(gClusterTimeMethods)},
+};
+
+static jboolean KernelCpuUidBpfMapReader_startTrackingBpfTimes(JNIEnv *, jobject) {
+ return android::bpf::startTrackingUidTimes();
+}
+
+int register_com_android_internal_os_KernelCpuUidBpfMapReader(JNIEnv *env) {
+ gSparseArrayClassInfo.clazz = FindClassOrDie(env, "android/util/SparseArray");
+ gSparseArrayClassInfo.clazz = MakeGlobalRefOrDie(env, gSparseArrayClassInfo.clazz);
+ gSparseArrayClassInfo.put =
+ GetMethodIDOrDie(env, gSparseArrayClassInfo.clazz, "put", "(ILjava/lang/Object;)V");
+ gSparseArrayClassInfo.get =
+ GetMethodIDOrDie(env, gSparseArrayClassInfo.clazz, "get", "(I)Ljava/lang/Object;");
+ constexpr auto readerName = "com/android/internal/os/KernelCpuUidBpfMapReader";
+ constexpr JNINativeMethod method = {"startTrackingBpfTimes", "()Z",
+ (void *)KernelCpuUidBpfMapReader_startTrackingBpfTimes};
+
+ int ret = RegisterMethodsOrDie(env, readerName, &method, 1);
+ if (ret < 0) return ret;
+ auto c = FindClassOrDie(env, readerName);
+ gmData = GetFieldIDOrDie(env, c, "mData", "Landroid/util/SparseArray;");
+
+ for (const auto &m : gAllMethods) {
+ auto fullName = android::base::StringPrintf("%s$%s", readerName, m.name);
+ ret = RegisterMethodsOrDie(env, fullName.c_str(), m.methods, m.numMethods);
+ if (ret < 0) break;
+ }
+ return ret;
+}
+
+} // namespace android
diff --git a/core/jni/com_android_internal_os_KernelSingleUidTimeReader.cpp b/core/jni/com_android_internal_os_KernelSingleUidTimeReader.cpp
new file mode 100644
index 0000000..c0ecf33
--- /dev/null
+++ b/core/jni/com_android_internal_os_KernelSingleUidTimeReader.cpp
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "core_jni_helpers.h"
+
+#include <cputimeinstate.h>
+
+namespace android {
+
+static constexpr uint64_t NSEC_PER_MSEC = 1000000;
+
+static jlongArray copyVecsToArray(JNIEnv *env, std::vector<std::vector<uint64_t>> &vec) {
+ jsize s = 0;
+ for (const auto &subVec : vec) s += subVec.size();
+ jlongArray ar = env->NewLongArray(s);
+ jsize start = 0;
+ for (auto &subVec : vec) {
+ for (uint32_t i = 0; i < subVec.size(); ++i) subVec[i] /= NSEC_PER_MSEC;
+ env->SetLongArrayRegion(ar, start, subVec.size(),
+ reinterpret_cast<const jlong*>(subVec.data()));
+ start += subVec.size();
+ }
+ return ar;
+}
+
+static jlongArray getUidCpuFreqTimeMs(JNIEnv *env, jclass, jint uid) {
+ auto out = android::bpf::getUidCpuFreqTimes(uid);
+ if (!out) return env->NewLongArray(0);
+ return copyVecsToArray(env, out.value());
+}
+
+static const JNINativeMethod g_single_methods[] = {
+ {"readBpfData", "(I)[J", (void *)getUidCpuFreqTimeMs},
+};
+
+int register_com_android_internal_os_KernelSingleUidTimeReader(JNIEnv *env) {
+ return RegisterMethodsOrDie(env, "com/android/internal/os/KernelSingleUidTimeReader$Injector",
+ g_single_methods, NELEM(g_single_methods));
+}
+
+}
diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index 7e4a16d..ea58cbd 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -315,6 +315,8 @@
enum RuntimeFlags : uint32_t {
DEBUG_ENABLE_JDWP = 1,
PROFILE_FROM_SHELL = 1 << 15,
+ MEMORY_TAG_LEVEL_MASK = (1 << 19) | (1 << 20),
+ MEMORY_TAG_LEVEL_TBI = 1 << 19,
};
enum UnsolicitedZygoteMessageTypes : uint32_t {
@@ -1153,6 +1155,16 @@
}
}
+ HeapTaggingLevel heap_tagging_level;
+ switch (runtime_flags & RuntimeFlags::MEMORY_TAG_LEVEL_MASK) {
+ case RuntimeFlags::MEMORY_TAG_LEVEL_TBI:
+ heap_tagging_level = M_HEAP_TAGGING_LEVEL_TBI;
+ break;
+ default:
+ heap_tagging_level = M_HEAP_TAGGING_LEVEL_NONE;
+ }
+ android_mallopt(M_SET_HEAP_TAGGING_LEVEL, &heap_tagging_level, sizeof(heap_tagging_level));
+
if (NeedsNoRandomizeWorkaround()) {
// Work around ARM kernel ASLR lossage (http://b/5817320).
int old_personality = personality(0xffffffff);
diff --git a/core/proto/android/app/settings_enums.proto b/core/proto/android/app/settings_enums.proto
index c217caf..5690af5 100644
--- a/core/proto/android/app/settings_enums.proto
+++ b/core/proto/android/app/settings_enums.proto
@@ -2437,4 +2437,10 @@
// > Pair device with QR code > Scan QR code > Pairing device dialog
// CATEGORY: SETTINGS
ADB_WIRELESS_DEVICE_QR_PAIRING_DIALOG = 1833;
+
+ // OPEN: Settings > Developer Options > Wireless debugging
+ // > Click on paired device
+ // CATEGORY: SETTINGS
+ // OS: R
+ ADB_WIRELESS_DEVICE_DETAILS = 1836;
}
diff --git a/core/proto/android/stats/dnsresolver/dns_resolver.proto b/core/proto/android/stats/dnsresolver/dns_resolver.proto
index 9eaabfb..15ef122 100644
--- a/core/proto/android/stats/dnsresolver/dns_resolver.proto
+++ b/core/proto/android/stats/dnsresolver/dns_resolver.proto
@@ -169,12 +169,20 @@
NT_BLUETOOTH = 3;
// Indicates this network uses an Ethernet transport.
NT_ETHERNET = 4;
- // Indicates this network uses a VPN transport.
- NT_VPN = 5;
+ // Indicates this network uses a VPN transport, now deprecated.
+ NT_VPN = 5 [deprecated=true];
// Indicates this network uses a Wi-Fi Aware transport.
NT_WIFI_AWARE = 6;
// Indicates this network uses a LoWPAN transport.
NT_LOWPAN = 7;
+ // Indicates this network uses a Cellular+VPN transport.
+ NT_CELLULAR_VPN = 8;
+ // Indicates this network uses a Wi-Fi+VPN transport.
+ NT_WIFI_VPN = 9;
+ // Indicates this network uses a Bluetooth+VPN transport.
+ NT_BLUETOOTH_VPN = 10;
+ // Indicates this network uses an Ethernet+VPN transport.
+ NT_ETHERNET_VPN = 11;
}
enum CacheStatus{
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 64dcfa7..e8cfa27 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -391,6 +391,9 @@
<protected-broadcast android:name="android.intent.action.AIRPLANE_MODE" />
<protected-broadcast android:name="android.intent.action.ADVANCED_SETTINGS" />
<protected-broadcast android:name="android.intent.action.APPLICATION_RESTRICTIONS_CHANGED" />
+ <protected-broadcast android:name="com.android.server.adb.WIRELESS_DEBUG_PAIRED_DEVICES" />
+ <protected-broadcast android:name="com.android.server.adb.WIRELESS_DEBUG_PAIRING_RESULT" />
+ <protected-broadcast android:name="com.android.server.adb.WIRELESS_DEBUG_STATUS" />
<!-- Legacy -->
<protected-broadcast android:name="android.intent.action.ACTION_IDLE_MAINTENANCE_START" />
@@ -1640,6 +1643,7 @@
<!-- Allows Settings and SystemUI to call methods in Networking services
<p>Not for use by third-party or privileged applications.
+ @SystemApi @TestApi
@hide This should only be used by Settings and SystemUI.
-->
<permission android:name="android.permission.NETWORK_SETTINGS"
@@ -1782,6 +1786,9 @@
android:protectionLevel="normal" />
<!-- @SystemApi Allows an internal user to use privileged SecureElement APIs.
+ Applications holding this permission can access OMAPI reset system API
+ and bypass OMAPI AccessControlEnforcer.
+ <p>Not for use by third-party applications.
@hide -->
<permission android:name="android.permission.SECURE_ELEMENT_PRIVILEGED"
android:protectionLevel="signature|privileged" />
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 0541955..1b45d40 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -2718,6 +2718,18 @@
<string name="config_customAdbPublicKeyConfirmationSecondaryUserComponent"
>com.android.systemui/com.android.systemui.usb.UsbDebuggingSecondaryUserActivity</string>
+ <!-- Name of the activity or service that prompts the user to reject, accept, or whitelist
+ a wireless network for wireless debugging.
+ Can be customized for other product types -->
+ <string name="config_customAdbWifiNetworkConfirmationComponent"
+ >com.android.systemui/com.android.systemui.wifi.WifiDebuggingActivity</string>
+
+ <!-- Name of the activity that prompts the secondary user to acknowledge she/he needs to
+ switch to the primary user to enable wireless debugging.
+ Can be customized for other product types -->
+ <string name="config_customAdbWifiNetworkConfirmationSecondaryUserComponent"
+ >com.android.systemui/com.android.systemui.wifi.WifiDebuggingSecondaryUserActivity</string>
+
<!-- Name of the dialog that is used to request the user's consent for a Platform VPN -->
<string name="config_platformVpnConfirmDialogComponent" translatable="false"
>com.android.vpndialogs/com.android.vpndialogs.PlatformVpnConfirmDialog</string>
@@ -3031,6 +3043,10 @@
<!-- Whether to use voip audio mode for ims call -->
<bool name="config_use_voip_mode_for_ims">false</bool>
+ <!-- Boolean indicating USSD over IMS is allowed.
+ If it is not supported due to modem limitations, USSD send over the CS pipe instead.-->
+ <bool name="config_allow_ussd_over_ims">false</bool>
+
<!-- String array containing numbers that shouldn't be logged. Country-specific. -->
<string-array name="unloggable_phone_numbers" />
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 9bc0d96..1758702 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -3642,6 +3642,12 @@
<!-- Message of notification shown when ADB is actively connected to the phone. -->
<string name="adb_active_notification_message">Tap to turn off USB debugging</string>
<string name="adb_active_notification_message" product="tv">Select to disable USB debugging.</string>
+ <!-- Title of notification shown when ADB Wireless is actively connected to the phone. [CHAR LIMIT=NONE] -->
+ <string name="adbwifi_active_notification_title">Wireless debugging connected</string>
+ <!-- Message of notification shown when ADB Wireless is actively connected to the phone. [CHAR LIMIT=NONE] -->
+ <string name="adbwifi_active_notification_message">Tap to turn off wireless debugging</string>
+ <!-- Message of notification shown when ADB Wireless is actively connected to the TV. [CHAR LIMIT=NONE] -->
+ <string name="adbwifi_active_notification_message" product="tv">Select to disable wireless debugging.</string>
<!-- Title of notification shown when Test Harness Mode is enabled. [CHAR LIMIT=NONE] -->
<string name="test_harness_mode_notification_title">Test Harness Mode enabled</string>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index d1e449e..188d924 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2090,6 +2090,8 @@
<java-symbol type="string" name="accessibility_binding_label" />
<java-symbol type="string" name="adb_active_notification_message" />
<java-symbol type="string" name="adb_active_notification_title" />
+ <java-symbol type="string" name="adbwifi_active_notification_message" />
+ <java-symbol type="string" name="adbwifi_active_notification_title" />
<java-symbol type="string" name="test_harness_mode_notification_title" />
<java-symbol type="string" name="test_harness_mode_notification_message" />
<java-symbol type="string" name="taking_remote_bugreport_notification_title" />
@@ -2232,6 +2234,8 @@
<java-symbol type="fraction" name="config_maximumScreenDimRatio" />
<java-symbol type="string" name="config_customAdbPublicKeyConfirmationComponent" />
<java-symbol type="string" name="config_customAdbPublicKeyConfirmationSecondaryUserComponent" />
+ <java-symbol type="string" name="config_customAdbWifiNetworkConfirmationComponent" />
+ <java-symbol type="string" name="config_customAdbWifiNetworkConfirmationSecondaryUserComponent" />
<java-symbol type="string" name="config_customVpnConfirmDialogComponent" />
<java-symbol type="string" name="config_customVpnAlwaysOnDisconnectedDialogComponent" />
<java-symbol type="string" name="config_platformVpnConfirmDialogComponent" />
@@ -2623,6 +2627,7 @@
<java-symbol type="bool" name="config_device_wfc_ims_available" />
<java-symbol type="bool" name="config_carrier_wfc_ims_available" />
<java-symbol type="bool" name="config_use_voip_mode_for_ims" />
+ <java-symbol type="bool" name="config_allow_ussd_over_ims" />
<java-symbol type="attr" name="touchscreenBlocksFocus" />
<java-symbol type="layout" name="resolver_list_with_default" />
<java-symbol type="string" name="activity_resolver_set_always" />
diff --git a/core/res/res/xml/power_profile.xml b/core/res/res/xml/power_profile.xml
index 899d630..d8ec72f 100644
--- a/core/res/res/xml/power_profile.xml
+++ b/core/res/res/xml/power_profile.xml
@@ -51,6 +51,12 @@
<value>0.1</value> <!-- ~1mA -->
</array>
+ <!-- Additional power consumption by CPU excluding cluster and core when
+ running -->
+ <array name="cpu.active">
+ <value>0.1</value>
+ </array>
+
<!-- A list of heterogeneous CPU clusters, where the value for each cluster represents the
number of CPU cores for that cluster.
diff --git a/core/tests/coretests/src/android/os/BundleTest.java b/core/tests/coretests/src/android/os/BundleTest.java
index e4dc993..4cc70ba 100644
--- a/core/tests/coretests/src/android/os/BundleTest.java
+++ b/core/tests/coretests/src/android/os/BundleTest.java
@@ -30,12 +30,23 @@
* Unit tests for bundle that requires accessing hidden APS. Tests that can be written only with
* public APIs should go in the CTS counterpart.
*
- * Run with:
- * bit FrameworksCoreTests:android.os.BundleTest
+ * Run with: atest FrameworksCoreTests:android.os.BundleTest
*/
@SmallTest
@RunWith(AndroidJUnit4.class)
public class BundleTest {
+
+ /**
+ * Take a bundle, write it to a parcel and return the parcel.
+ */
+ private Parcel getParcelledBundle(Bundle bundle) {
+ final Parcel p = Parcel.obtain();
+ // Don't use p.writeParcelabe(), which would write the creator, which we don't need.
+ bundle.writeToParcel(p, 0);
+ p.setDataPosition(0);
+ return p;
+ }
+
/**
* Create a test bundle, parcel it and return the parcel.
*/
@@ -48,12 +59,7 @@
pipe[1].close();
source.putParcelable("fd", pipe[0]);
}
- final Parcel p = Parcel.obtain();
- // Don't use p.writeParcelabe(), which would write the creator, which we don't need.
- source.writeToParcel(p, 0);
- p.setDataPosition(0);
-
- return p;
+ return getParcelledBundle(source);
}
/**
@@ -137,4 +143,78 @@
checkBundle(b, withFd);
p.recycle();
}
+
+ @Test
+ public void kindofEquals_bothUnparcelled_same() {
+ Bundle bundle1 = new Bundle();
+ bundle1.putString("StringKey", "S");
+ bundle1.putInt("IntKey", 2);
+
+ Bundle bundle2 = new Bundle();
+ bundle2.putString("StringKey", "S");
+ bundle2.putInt("IntKey", 2);
+
+ assertTrue(BaseBundle.kindofEquals(bundle1, bundle2));
+ }
+
+ @Test
+ public void kindofEquals_bothUnparcelled_different() {
+ Bundle bundle1 = new Bundle();
+ bundle1.putString("StringKey", "S");
+ bundle1.putInt("IntKey", 2);
+
+ Bundle bundle2 = new Bundle();
+ bundle2.putString("StringKey", "T");
+ bundle2.putLong("LongKey", 30L);
+
+ assertFalse(BaseBundle.kindofEquals(bundle1, bundle2));
+ }
+
+ @Test
+ public void kindofEquals_bothParcelled_same() {
+ Bundle bundle1 = new Bundle();
+ bundle1.putString("StringKey", "S");
+ bundle1.putInt("IntKey", 2);
+ bundle1.readFromParcel(getParcelledBundle(bundle1));
+
+ Bundle bundle2 = new Bundle();
+ bundle2.putString("StringKey", "S");
+ bundle2.putInt("IntKey", 2);
+ bundle2.readFromParcel(getParcelledBundle(bundle2));
+
+ assertTrue(bundle1.isParcelled());
+ assertTrue(bundle2.isParcelled());
+ assertTrue(BaseBundle.kindofEquals(bundle1, bundle2));
+ }
+
+ @Test
+ public void kindofEquals_bothParcelled_different() {
+ Bundle bundle1 = new Bundle();
+ bundle1.putString("StringKey", "S");
+ bundle1.putInt("IntKey", 2);
+ bundle1.readFromParcel(getParcelledBundle(bundle1));
+
+ Bundle bundle2 = new Bundle();
+ bundle2.putString("StringKey", "T");
+ bundle2.putLong("LongKey", 5);
+ bundle2.readFromParcel(getParcelledBundle(bundle2));
+
+ assertTrue(bundle1.isParcelled());
+ assertTrue(bundle2.isParcelled());
+ assertFalse(BaseBundle.kindofEquals(bundle1, bundle2));
+ }
+
+ @Test
+ public void kindofEquals_ParcelledUnparcelled_empty() {
+ Bundle bundle1 = new Bundle();
+ bundle1.readFromParcel(getParcelledBundle(bundle1));
+
+ Bundle bundle2 = new Bundle();
+
+ assertTrue(bundle1.isParcelled());
+ assertFalse(bundle2.isParcelled());
+ // Even though one is parcelled and the other is not, both are empty, so it should
+ // return true
+ assertTrue(BaseBundle.kindofEquals(bundle1, bundle2));
+ }
}
diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
index e60e555..4b1691d 100644
--- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java
+++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
@@ -104,6 +104,7 @@
Settings.Global.ADAPTIVE_BATTERY_MANAGEMENT_ENABLED,
Settings.Global.ADB_ALLOWED_CONNECTION_TIME,
Settings.Global.ADB_ENABLED,
+ Settings.Global.ADB_WIFI_ENABLED,
Settings.Global.ADD_USERS_WHEN_LOCKED,
Settings.Global.AIRPLANE_MODE_ON,
Settings.Global.AIRPLANE_MODE_RADIOS,
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java
index a6329298..2ad8e18 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java
@@ -40,6 +40,7 @@
BatteryStatsUserLifecycleTests.class,
KernelCpuProcStringReaderTest.class,
KernelCpuUidActiveTimeReaderTest.class,
+ KernelCpuUidBpfMapReaderTest.class,
KernelCpuUidClusterTimeReaderTest.class,
KernelCpuUidFreqTimeReaderTest.class,
KernelCpuUidUserSysTimeReaderTest.class,
diff --git a/core/tests/coretests/src/com/android/internal/os/KernelCpuUidActiveTimeReaderTest.java b/core/tests/coretests/src/com/android/internal/os/KernelCpuUidActiveTimeReaderTest.java
index 1b13a99..2ccd74e 100644
--- a/core/tests/coretests/src/com/android/internal/os/KernelCpuUidActiveTimeReaderTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/KernelCpuUidActiveTimeReaderTest.java
@@ -21,11 +21,11 @@
import android.content.Context;
import android.os.FileUtils;
+import android.util.SparseArray;
import android.util.SparseLongArray;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
import com.android.internal.os.KernelCpuUidTimeReader.KernelCpuUidActiveTimeReader;
@@ -33,11 +33,15 @@
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
import java.io.BufferedWriter;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
+import java.util.Arrays;
+import java.util.Collection;
import java.util.Random;
/**
@@ -46,14 +50,16 @@
* $ atest FrameworksCoreTests:com.android.internal.os.KernelCpuUidActiveTimeReaderTest
*/
@SmallTest
-@RunWith(AndroidJUnit4.class)
+@RunWith(Parameterized.class)
public class KernelCpuUidActiveTimeReaderTest {
private File mTestDir;
private File mTestFile;
private KernelCpuUidActiveTimeReader mReader;
+ private KernelCpuUidTestBpfMapReader mBpfMapReader;
private VerifiableCallback mCallback;
private Random mRand = new Random(12345);
+ protected boolean mUseBpf;
private final int mCpus = 4;
private final String mHeadline = "cpus: 4\n";
private final int[] mUids = {0, 1, 22, 333, 4444, 55555};
@@ -62,12 +68,22 @@
return InstrumentationRegistry.getContext();
}
+ @Parameters(name="useBpf={0}")
+ public static Collection<Object[]> data() {
+ return Arrays.asList(new Object[][] { {true}, {false} });
+ }
+
+ public KernelCpuUidActiveTimeReaderTest(boolean useBpf) {
+ mUseBpf = useBpf;
+ }
+
@Before
public void setUp() {
mTestDir = getContext().getDir("test", Context.MODE_PRIVATE);
mTestFile = new File(mTestDir, "test.file");
+ mBpfMapReader = new KernelCpuUidTestBpfMapReader();
mReader = new KernelCpuUidActiveTimeReader(
- new KernelCpuProcStringReader(mTestFile.getAbsolutePath()), false);
+ new KernelCpuProcStringReader(mTestFile.getAbsolutePath()), mBpfMapReader, false);
mCallback = new VerifiableCallback();
}
@@ -80,7 +96,7 @@
@Test
public void testReadDelta() throws Exception {
final long[][] times = increaseTime(new long[mUids.length][mCpus]);
- writeToFile(mHeadline + uidLines(mUids, times));
+ setCpusAndData(times);
mReader.readDelta(mCallback);
for (int i = 0; i < mUids.length; ++i) {
mCallback.verify(mUids[i], getActiveTime(times[i]));
@@ -90,7 +106,7 @@
// Verify that a second call will only return deltas.
mCallback.clear();
final long[][] newTimes1 = increaseTime(times);
- writeToFile(mHeadline + uidLines(mUids, newTimes1));
+ setCpusAndData(newTimes1);
mReader.readDelta(mCallback);
for (int i = 0; i < mUids.length; ++i) {
mCallback.verify(mUids[i], getActiveTime(newTimes1[i]) - getActiveTime(times[i]));
@@ -105,7 +121,7 @@
// Verify that calling with a null callback doesn't result in any crashes
mCallback.clear();
final long[][] newTimes2 = increaseTime(newTimes1);
- writeToFile(mHeadline + uidLines(mUids, newTimes2));
+ setCpusAndData(newTimes2);
mReader.readDelta(null);
mCallback.verifyNoMoreInteractions();
@@ -113,19 +129,20 @@
// the previous call had null callback.
mCallback.clear();
final long[][] newTimes3 = increaseTime(newTimes2);
+ setCpusAndData(newTimes3);
writeToFile(mHeadline + uidLines(mUids, newTimes3));
mReader.readDelta(mCallback);
for (int i = 0; i < mUids.length; ++i) {
mCallback.verify(mUids[i], getActiveTime(newTimes3[i]) - getActiveTime(newTimes2[i]));
}
mCallback.verifyNoMoreInteractions();
- assertTrue(mTestFile.delete());
+ clearCpusAndData();
}
@Test
public void testReadAbsolute() throws Exception {
final long[][] times1 = increaseTime(new long[mUids.length][mCpus]);
- writeToFile(mHeadline + uidLines(mUids, times1));
+ setCpusAndData(times1);
mReader.readAbsolute(mCallback);
for (int i = 0; i < mUids.length; i++) {
mCallback.verify(mUids[i], getActiveTime(times1[i]));
@@ -135,19 +152,19 @@
// Verify that a second call should still return absolute values
mCallback.clear();
final long[][] times2 = increaseTime(times1);
- writeToFile(mHeadline + uidLines(mUids, times2));
+ setCpusAndData(times2);
mReader.readAbsolute(mCallback);
for (int i = 0; i < mUids.length; i++) {
mCallback.verify(mUids[i], getActiveTime(times2[i]));
}
mCallback.verifyNoMoreInteractions();
- assertTrue(mTestFile.delete());
+ clearCpusAndData();
}
@Test
public void testReadDeltaDecreasedTime() throws Exception {
final long[][] times1 = increaseTime(new long[mUids.length][mCpus]);
- writeToFile(mHeadline + uidLines(mUids, times1));
+ setCpusAndData(times1);
mReader.readDelta(mCallback);
// Verify that there should not be a callback for a particular UID if its time decreases.
@@ -155,19 +172,19 @@
final long[][] times2 = increaseTime(times1);
System.arraycopy(times1[0], 0, times2[0], 0, mCpus);
times2[0][0] = 100;
- writeToFile(mHeadline + uidLines(mUids, times2));
+ setCpusAndData(times2);
mReader.readDelta(mCallback);
for (int i = 1; i < mUids.length; i++) {
mCallback.verify(mUids[i], getActiveTime(times2[i]) - getActiveTime(times1[i]));
}
mCallback.verifyNoMoreInteractions();
- assertTrue(mTestFile.delete());
+ clearCpusAndData();
// Verify that the internal state was not modified.
mCallback.clear();
final long[][] times3 = increaseTime(times2);
times3[0] = increaseTime(times1)[0];
- writeToFile(mHeadline + uidLines(mUids, times3));
+ setCpusAndData(times3);
mReader.readDelta(mCallback);
mCallback.verify(mUids[0], getActiveTime(times3[0]) - getActiveTime(times1[0]));
for (int i = 1; i < mUids.length; i++) {
@@ -179,26 +196,26 @@
@Test
public void testReadDeltaNegativeTime() throws Exception {
final long[][] times1 = increaseTime(new long[mUids.length][mCpus]);
- writeToFile(mHeadline + uidLines(mUids, times1));
+ setCpusAndData(times1);
mReader.readDelta(mCallback);
// Verify that there should not be a callback for a particular UID if its time is -ve.
mCallback.clear();
final long[][] times2 = increaseTime(times1);
times2[0][0] *= -1;
- writeToFile(mHeadline + uidLines(mUids, times2));
+ setCpusAndData(times2);
mReader.readDelta(mCallback);
for (int i = 1; i < mUids.length; i++) {
mCallback.verify(mUids[i], getActiveTime(times2[i]) - getActiveTime(times1[i]));
}
mCallback.verifyNoMoreInteractions();
- assertTrue(mTestFile.delete());
+ clearCpusAndData();
// Verify that the internal state was not modified.
mCallback.clear();
final long[][] times3 = increaseTime(times2);
times3[0] = increaseTime(times1)[0];
- writeToFile(mHeadline + uidLines(mUids, times3));
+ setCpusAndData(times3);
mReader.readDelta(mCallback);
mCallback.verify(mUids[0], getActiveTime(times3[0]) - getActiveTime(times1[0]));
for (int i = 1; i < mUids.length; i++) {
@@ -207,6 +224,28 @@
mCallback.verifyNoMoreInteractions();
}
+ private void setCpusAndData(long[][] times) throws IOException {
+ if (mUseBpf) {
+ mBpfMapReader.setCpus(new long[]{ mCpus });
+ SparseArray<long[]> data = new SparseArray<>();
+ for (int i = 0; i < mUids.length; i++) {
+ data.put(mUids[i], times[i]);
+ }
+ mBpfMapReader.setData(data);
+ } else {
+ writeToFile(mHeadline + uidLines(mUids, times));
+ }
+ }
+
+ private void clearCpusAndData() {
+ if (mUseBpf) {
+ mBpfMapReader.setCpus(null);
+ mBpfMapReader.setData(new SparseArray<>());
+ } else {
+ assertTrue(mTestFile.delete());
+ }
+ }
+
private String uidLines(int[] uids, long[][] times) {
StringBuffer sb = new StringBuffer();
for (int i = 0; i < uids.length; i++) {
@@ -261,4 +300,36 @@
assertEquals(0, mData.size());
}
}
+
+ private class KernelCpuUidTestBpfMapReader extends KernelCpuUidBpfMapReader {
+ private long[] mCpus;
+ private SparseArray<long[]> mNewData = new SparseArray<>();
+
+ public void setData(SparseArray<long[]> data) {
+ mNewData = data;
+ }
+
+ public void setCpus(long[] cpus) {
+ mCpus = cpus;
+ }
+
+ @Override
+ public final boolean startTrackingBpfTimes() {
+ return true;
+ }
+
+ @Override
+ protected final boolean readBpfData() {
+ if (!mUseBpf) {
+ return false;
+ }
+ mData = mNewData;
+ return true;
+ }
+
+ @Override
+ public final long[] getDataDimensions() {
+ return mCpus;
+ }
+ }
}
diff --git a/core/tests/coretests/src/com/android/internal/os/KernelCpuUidBpfMapReaderTest.java b/core/tests/coretests/src/com/android/internal/os/KernelCpuUidBpfMapReaderTest.java
new file mode 100644
index 0000000..257b388
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/os/KernelCpuUidBpfMapReaderTest.java
@@ -0,0 +1,237 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.os;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import android.content.Context;
+import android.util.SparseArray;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+import androidx.test.runner.AndroidJUnit4;
+import org.junit.runner.RunWith;
+
+import com.android.internal.os.KernelCpuUidBpfMapReader.BpfMapIterator;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Random;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
+import java.util.stream.IntStream;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class KernelCpuUidBpfMapReaderTest {
+ private Random mRand = new Random(12345);
+ private KernelCpuUidTestBpfMapReader mReader;
+
+ private Context getContext() {
+ return InstrumentationRegistry.getContext();
+ }
+
+ @Before
+ public void setUp() {
+ mReader = new KernelCpuUidTestBpfMapReader();
+ }
+
+ /**
+ * Tests that reading returns null if readBpfData() fails.
+ */
+ @Test
+ public void testUnsuccessfulRead() {
+ assertEquals(null, mReader.open());
+ }
+
+ /**
+ * Tests that reading will always return null after 5 failures.
+ */
+ @Test
+ public void testReadErrorsLimit() {
+ for (int i = 0; i < 3; i++) {
+ try (BpfMapIterator iter = mReader.open()) {
+ assertNull(iter);
+ }
+ }
+
+ SparseArray<long[]> data = new SparseArray<>();
+ long[] times = {2};
+ data.put(1, new long[]{2});
+ mReader.setData(data);
+ testOpenAndReadData(data);
+
+ mReader.setData(null);
+ for (int i = 0; i < 3; i++) {
+ try (BpfMapIterator iter = mReader.open(true)) {
+ assertNull(iter);
+ }
+ }
+ mReader.setData(data);
+ try (BpfMapIterator iter = mReader.open(true)) {
+ assertNull(iter);
+ }
+ }
+
+ /** Tests getNextUid functionality. */
+ @Test
+ public void testGetNextUid() {
+ final SparseArray<long[]> data = getTestSparseArray(800, 50);
+ mReader.setData(data);
+ testOpenAndReadData(data);
+ }
+
+ @Test
+ public void testConcurrent() throws Exception {
+ final SparseArray<long[]> data = getTestSparseArray(200, 50);
+ final SparseArray<long[]> data1 = getTestSparseArray(180, 70);
+ final List<Throwable> errs = Collections.synchronizedList(new ArrayList<>());
+ mReader.setData(data);
+ // An additional thread for modifying the data.
+ ScheduledExecutorService threadPool = Executors.newScheduledThreadPool(11);
+ final CountDownLatch ready = new CountDownLatch(10);
+ final CountDownLatch start = new CountDownLatch(1);
+ final CountDownLatch modify = new CountDownLatch(1);
+ final CountDownLatch done = new CountDownLatch(10);
+
+ for (int i = 0; i < 5; i++) {
+ threadPool.submit(() -> {
+ ready.countDown();
+ try {
+ start.await();
+ testOpenAndReadData(data);
+ } catch (Throwable e) {
+ errs.add(e);
+ } finally {
+ done.countDown();
+ }
+ });
+ threadPool.submit(() -> {
+ ready.countDown();
+ try {
+ start.await();
+ // Wait for data modification.
+ modify.await();
+ testOpenAndReadData(data1);
+ } catch (Throwable e) {
+ errs.add(e);
+ } finally {
+ done.countDown();
+ }
+ });
+ }
+
+ assertTrue("Prep timed out", ready.await(100, TimeUnit.MILLISECONDS));
+ start.countDown();
+
+ threadPool.schedule(() -> {
+ mReader.setData(data1);
+ modify.countDown();
+ }, 600, TimeUnit.MILLISECONDS);
+
+ assertTrue("Execution timed out", done.await(3, TimeUnit.SECONDS));
+ threadPool.shutdownNow();
+
+ StringWriter sw = new StringWriter();
+ PrintWriter pw = new PrintWriter(sw);
+ errs.forEach(e -> e.printStackTrace(pw));
+
+ assertTrue("All Exceptions:\n" + sw.toString(), errs.isEmpty());
+ }
+
+ @Test
+ public void testRemoveUidsInRange() {
+ final SparseArray<long[]> data = getTestSparseArray(200, 50);
+ mReader.setData(data);
+ testOpenAndReadData(data);
+ SparseArray<long[]> changedData = new SparseArray<>();
+ for (int i = 6; i < 200; i++) {
+ changedData.put(i, data.get(i));
+ }
+ mReader.removeUidsInRange(0, 5);
+ testOpenAndReadData(changedData);
+ }
+
+ private void testOpenAndReadData(SparseArray<long[]> expectedData) {
+ try (BpfMapIterator iter = mReader.open()) {
+ long[] actual;
+ if (expectedData.size() > 0) {
+ actual = new long[expectedData.valueAt(0).length + 1];
+ } else {
+ actual = new long[0];
+ }
+ for (int i = 0; i < expectedData.size(); i++) {
+ assertTrue(iter.getNextUid(actual));
+ assertEquals(expectedData.keyAt(i), actual[0]);
+ assertArrayEquals(expectedData.valueAt(i), Arrays.copyOfRange(actual, 1, actual.length));
+ }
+ assertFalse(iter.getNextUid(actual));
+ assertFalse(iter.getNextUid(actual));
+ }
+ }
+
+
+ private SparseArray<long[]> getTestSparseArray(int uids, int numPerUid) {
+ SparseArray<long[]> data = new SparseArray<>();
+ for (int i = 0; i < uids; i++) {
+ data.put(i, mRand.longs(numPerUid, 0, Long.MAX_VALUE).toArray());
+ }
+ return data;
+ }
+
+
+ private class KernelCpuUidTestBpfMapReader extends KernelCpuUidBpfMapReader {
+ private SparseArray<long[]> mNewData;
+
+ public final void setData(SparseArray<long[]> newData) {
+ mNewData = newData;
+ }
+
+ @Override
+ public final boolean startTrackingBpfTimes() {
+ return true;
+ }
+
+ @Override
+ public final boolean readBpfData() {
+ if (mNewData == null) {
+ return false;
+ }
+ mData = mNewData;
+ return true;
+ }
+
+ @Override
+ public final long[] getDataDimensions() {
+ return null;
+ }
+
+ }
+}
diff --git a/core/tests/coretests/src/com/android/internal/os/KernelCpuUidClusterTimeReaderTest.java b/core/tests/coretests/src/com/android/internal/os/KernelCpuUidClusterTimeReaderTest.java
index 2ea80da..a0dab28 100644
--- a/core/tests/coretests/src/com/android/internal/os/KernelCpuUidClusterTimeReaderTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/KernelCpuUidClusterTimeReaderTest.java
@@ -27,7 +27,6 @@
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
import com.android.internal.os.KernelCpuUidTimeReader.KernelCpuUidClusterTimeReader;
@@ -35,11 +34,15 @@
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
import java.io.BufferedWriter;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
+import java.util.Arrays;
+import java.util.Collection;
import java.util.Random;
/**
@@ -48,28 +51,42 @@
* $ atest FrameworksCoreTests:com.android.internal.os.KernelCpuUidClusterTimeReaderTest
*/
@SmallTest
-@RunWith(AndroidJUnit4.class)
+@RunWith(Parameterized.class)
public class KernelCpuUidClusterTimeReaderTest {
private File mTestDir;
private File mTestFile;
private KernelCpuUidClusterTimeReader mReader;
+ private KernelCpuUidTestBpfMapReader mBpfMapReader;
private VerifiableCallback mCallback;
private Random mRand = new Random(12345);
+ protected boolean mUseBpf;
private final int mCpus = 6;
private final String mHeadline = "policy0: 4 policy4: 2\n";
+ private final long[] mCores = {4, 2};
private final int[] mUids = {0, 1, 22, 333, 4444, 55555};
private Context getContext() {
return InstrumentationRegistry.getContext();
}
+ @Parameters(name="useBpf={0}")
+ public static Collection<Object[]> data() {
+ return Arrays.asList(new Object[][] { {true}, {false} });
+ }
+
+ public KernelCpuUidClusterTimeReaderTest(boolean useBpf) {
+ mUseBpf = useBpf;
+ }
+
@Before
public void setUp() {
mTestDir = getContext().getDir("test", Context.MODE_PRIVATE);
mTestFile = new File(mTestDir, "test.file");
+ mBpfMapReader = new KernelCpuUidTestBpfMapReader();
mReader = new KernelCpuUidClusterTimeReader(
- new KernelCpuProcStringReader(mTestFile.getAbsolutePath()), false);
+ new KernelCpuProcStringReader(mTestFile.getAbsolutePath()), mBpfMapReader,
+ false);
mCallback = new VerifiableCallback();
}
@@ -82,7 +99,7 @@
@Test
public void testReadDelta() throws Exception {
final long[][] times1 = increaseTime(new long[mUids.length][mCpus]);
- writeToFile(mHeadline + uidLines(mUids, times1));
+ setCoresAndData(times1);
mReader.readDelta(mCallback);
for (int i = 0; i < mUids.length; ++i) {
mCallback.verify(mUids[i], clusterTime(times1[i]));
@@ -92,7 +109,7 @@
// Verify that a second call will only return deltas.
mCallback.clear();
final long[][] times2 = increaseTime(times1);
- writeToFile(mHeadline + uidLines(mUids, times2));
+ setCoresAndData(times2);
mReader.readDelta(mCallback);
for (int i = 0; i < mUids.length; ++i) {
mCallback.verify(mUids[i], subtract(clusterTime(times2[i]), clusterTime(times1[i])));
@@ -107,7 +124,7 @@
// Verify that calling with a null callback doesn't result in any crashes
mCallback.clear();
final long[][] times3 = increaseTime(times2);
- writeToFile(mHeadline + uidLines(mUids, times3));
+ setCoresAndData(times3);
mReader.readDelta(null);
mCallback.verifyNoMoreInteractions();
@@ -115,19 +132,19 @@
// the previous call had null callback.
mCallback.clear();
final long[][] times4 = increaseTime(times3);
- writeToFile(mHeadline + uidLines(mUids, times4));
+ setCoresAndData(times4);
mReader.readDelta(mCallback);
for (int i = 0; i < mUids.length; ++i) {
mCallback.verify(mUids[i], subtract(clusterTime(times4[i]), clusterTime(times3[i])));
}
mCallback.verifyNoMoreInteractions();
- assertTrue(mTestFile.delete());
+ clearCoresAndData();
}
@Test
public void testReadAbsolute() throws Exception {
final long[][] times1 = increaseTime(new long[mUids.length][mCpus]);
- writeToFile(mHeadline + uidLines(mUids, times1));
+ setCoresAndData(times1);
mReader.readAbsolute(mCallback);
for (int i = 0; i < mUids.length; i++) {
mCallback.verify(mUids[i], clusterTime(times1[i]));
@@ -137,19 +154,19 @@
// Verify that a second call should still return absolute values
mCallback.clear();
final long[][] times2 = increaseTime(times1);
- writeToFile(mHeadline + uidLines(mUids, times2));
+ setCoresAndData(times2);
mReader.readAbsolute(mCallback);
for (int i = 0; i < mUids.length; i++) {
mCallback.verify(mUids[i], clusterTime(times2[i]));
}
mCallback.verifyNoMoreInteractions();
- assertTrue(mTestFile.delete());
+ clearCoresAndData();
}
@Test
public void testReadDeltaDecreasedTime() throws Exception {
final long[][] times1 = increaseTime(new long[mUids.length][mCpus]);
- writeToFile(mHeadline + uidLines(mUids, times1));
+ setCoresAndData(times1);
mReader.readDelta(mCallback);
// Verify that there should not be a callback for a particular UID if its time decreases.
@@ -157,19 +174,19 @@
final long[][] times2 = increaseTime(times1);
System.arraycopy(times1[0], 0, times2[0], 0, mCpus);
times2[0][0] = 100;
- writeToFile(mHeadline + uidLines(mUids, times2));
+ setCoresAndData(times2);
mReader.readDelta(mCallback);
for (int i = 1; i < mUids.length; i++) {
mCallback.verify(mUids[i], subtract(clusterTime(times2[i]), clusterTime(times1[i])));
}
mCallback.verifyNoMoreInteractions();
- assertTrue(mTestFile.delete());
+ clearCoresAndData();
// Verify that the internal state was not modified.
mCallback.clear();
final long[][] times3 = increaseTime(times2);
times3[0] = increaseTime(times1)[0];
- writeToFile(mHeadline + uidLines(mUids, times3));
+ setCoresAndData(times3);
mReader.readDelta(mCallback);
mCallback.verify(mUids[0], subtract(clusterTime(times3[0]), clusterTime(times1[0])));
for (int i = 1; i < mUids.length; i++) {
@@ -181,26 +198,26 @@
@Test
public void testReadDeltaNegativeTime() throws Exception {
final long[][] times1 = increaseTime(new long[mUids.length][mCpus]);
- writeToFile(mHeadline + uidLines(mUids, times1));
+ setCoresAndData(times1);
mReader.readDelta(mCallback);
// Verify that there should not be a callback for a particular UID if its time decreases.
mCallback.clear();
final long[][] times2 = increaseTime(times1);
times2[0][0] *= -1;
- writeToFile(mHeadline + uidLines(mUids, times2));
+ setCoresAndData(times2);
mReader.readDelta(mCallback);
for (int i = 1; i < mUids.length; i++) {
mCallback.verify(mUids[i], subtract(clusterTime(times2[i]), clusterTime(times1[i])));
}
mCallback.verifyNoMoreInteractions();
- assertTrue(mTestFile.delete());
+ clearCoresAndData();
// Verify that the internal state was not modified.
mCallback.clear();
final long[][] times3 = increaseTime(times2);
times3[0] = increaseTime(times1)[0];
- writeToFile(mHeadline + uidLines(mUids, times3));
+ setCoresAndData(times3);
mReader.readDelta(mCallback);
mCallback.verify(mUids[0], subtract(clusterTime(times3[0]), clusterTime(times1[0])));
for (int i = 1; i < mUids.length; i++) {
@@ -209,6 +226,28 @@
mCallback.verifyNoMoreInteractions();
}
+ private void setCoresAndData(long[][] times) throws IOException {
+ if (mUseBpf) {
+ mBpfMapReader.setClusterCores(mCores);
+ SparseArray<long[]> data = new SparseArray<>();
+ for (int i = 0; i < mUids.length; i++) {
+ data.put(mUids[i], times[i]);
+ }
+ mBpfMapReader.setData(data);
+ } else {
+ writeToFile(mHeadline + uidLines(mUids, times));
+ }
+ }
+
+ private void clearCoresAndData() {
+ if (mUseBpf) {
+ mBpfMapReader.setClusterCores(null);
+ mBpfMapReader.setData(new SparseArray<>());
+ } else {
+ assertTrue(mTestFile.delete());
+ }
+ }
+
private long[] clusterTime(long[] times) {
// Assumes 4 + 2 cores
return new long[]{times[0] + times[1] / 2 + times[2] / 3 + times[3] / 4,
@@ -277,4 +316,36 @@
assertEquals(0, mData.size());
}
}
+
+ private class KernelCpuUidTestBpfMapReader extends KernelCpuUidBpfMapReader {
+ private long[] mClusterCores;
+ private SparseArray<long[]> mNewData = new SparseArray<>();
+
+ public void setData(SparseArray<long[]> data) {
+ mNewData = data;
+ }
+
+ public void setClusterCores(long[] cores) {
+ mClusterCores = cores;
+ }
+
+ @Override
+ public final boolean startTrackingBpfTimes() {
+ return true;
+ }
+
+ @Override
+ protected final boolean readBpfData() {
+ if (!mUseBpf) {
+ return false;
+ }
+ mData = mNewData;
+ return true;
+ }
+
+ @Override
+ public final long[] getDataDimensions() {
+ return mClusterCores;
+ }
+ }
}
diff --git a/core/tests/coretests/src/com/android/internal/os/KernelCpuUidFreqTimeReaderTest.java b/core/tests/coretests/src/com/android/internal/os/KernelCpuUidFreqTimeReaderTest.java
index 0b6fed3..c60a6d6 100644
--- a/core/tests/coretests/src/com/android/internal/os/KernelCpuUidFreqTimeReaderTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/KernelCpuUidFreqTimeReaderTest.java
@@ -29,7 +29,6 @@
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
import com.android.internal.os.KernelCpuUidTimeReader.KernelCpuUidFreqTimeReader;
@@ -37,6 +36,8 @@
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@@ -45,6 +46,7 @@
import java.io.IOException;
import java.nio.file.Files;
import java.util.Arrays;
+import java.util.Collection;
import java.util.Random;
/**
@@ -53,14 +55,16 @@
* $ atest FrameworksCoreTests:com.android.internal.os.KernelCpuUidFreqTimeReaderTest
*/
@SmallTest
-@RunWith(AndroidJUnit4.class)
+@RunWith(Parameterized.class)
public class KernelCpuUidFreqTimeReaderTest {
private File mTestDir;
private File mTestFile;
private KernelCpuUidFreqTimeReader mReader;
+ private KernelCpuUidTestBpfMapReader mBpfMapReader;
private VerifiableCallback mCallback;
@Mock
private PowerProfile mPowerProfile;
+ private boolean mUseBpf;
private Random mRand = new Random(12345);
private final int[] mUids = {0, 1, 22, 333, 4444, 55555};
@@ -69,13 +73,23 @@
return InstrumentationRegistry.getContext();
}
+ @Parameters(name="useBpf={0}")
+ public static Collection<Object[]> data() {
+ return Arrays.asList(new Object[][] { {true}, {false} });
+ }
+
+ public KernelCpuUidFreqTimeReaderTest(boolean useBpf) {
+ mUseBpf = useBpf;
+ }
+
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mTestDir = getContext().getDir("test", Context.MODE_PRIVATE);
mTestFile = new File(mTestDir, "test.file");
+ mBpfMapReader = new KernelCpuUidTestBpfMapReader();
mReader = new KernelCpuUidFreqTimeReader(mTestFile.getAbsolutePath(),
- new KernelCpuProcStringReader(mTestFile.getAbsolutePath()), false);
+ new KernelCpuProcStringReader(mTestFile.getAbsolutePath()), mBpfMapReader, false);
mCallback = new VerifiableCallback();
}
@@ -97,17 +111,17 @@
final int[][] numFreqs = {{3, 6}, {4, 5}, {3, 5, 4}, {3}};
for (int i = 0; i < freqs.length; ++i) {
mReader = new KernelCpuUidFreqTimeReader(mTestFile.getAbsolutePath(),
- new KernelCpuProcStringReader(mTestFile.getAbsolutePath()), false);
+ new KernelCpuProcStringReader(mTestFile.getAbsolutePath()), mBpfMapReader, false);
setCpuClusterFreqs(numClusters[i], numFreqs[i]);
- writeToFile(freqsLine(freqs[i]));
+ setFreqs(freqs[i]);
long[] actualFreqs = mReader.readFreqs(mPowerProfile);
assertArrayEquals(freqs[i], actualFreqs);
final String errMsg = String.format("Freqs=%s, nClusters=%d, nFreqs=%s",
Arrays.toString(freqs[i]), numClusters[i], Arrays.toString(numFreqs[i]));
assertFalse(errMsg, mReader.perClusterTimesAvailable());
- // Verify that a second call won't read the proc file again
- assertTrue(mTestFile.delete());
+ // Verify that a second call won't re-read the freqs
+ clearFreqsAndData();
actualFreqs = mReader.readFreqs(mPowerProfile);
assertArrayEquals(freqs[i], actualFreqs);
assertFalse(errMsg, mReader.perClusterTimesAvailable());
@@ -125,17 +139,17 @@
final int[][] numFreqs = {{4}, {3, 5}, {3, 5, 4}};
for (int i = 0; i < freqs.length; ++i) {
mReader = new KernelCpuUidFreqTimeReader(mTestFile.getAbsolutePath(),
- new KernelCpuProcStringReader(mTestFile.getAbsolutePath()), false);
+ new KernelCpuProcStringReader(mTestFile.getAbsolutePath()), mBpfMapReader, false);
setCpuClusterFreqs(numClusters[i], numFreqs[i]);
- writeToFile(freqsLine(freqs[i]));
+ setFreqs(freqs[i]);
long[] actualFreqs = mReader.readFreqs(mPowerProfile);
assertArrayEquals(freqs[i], actualFreqs);
final String errMsg = String.format("Freqs=%s, nClusters=%d, nFreqs=%s",
Arrays.toString(freqs[i]), numClusters[i], Arrays.toString(numFreqs[i]));
assertTrue(errMsg, mReader.perClusterTimesAvailable());
- // Verify that a second call won't read the proc file again
- assertTrue(mTestFile.delete());
+ // Verify that a second call won't re-read the freqs
+ clearFreqsAndData();
actualFreqs = mReader.readFreqs(mPowerProfile);
assertArrayEquals(freqs[i], actualFreqs);
assertTrue(errMsg, mReader.perClusterTimesAvailable());
@@ -147,7 +161,7 @@
final long[] freqs = {110, 123, 145, 167, 289, 997};
final long[][] times = increaseTime(new long[mUids.length][freqs.length]);
- writeToFile(freqsLine(freqs) + uidLines(mUids, times));
+ setFreqsAndData(freqs, times);
mReader.readDelta(mCallback);
for (int i = 0; i < mUids.length; ++i) {
mCallback.verify(mUids[i], times[i]);
@@ -155,14 +169,14 @@
mCallback.verifyNoMoreInteractions();
// Verify that readDelta also reads the frequencies if not already available.
- assertTrue(mTestFile.delete());
+ clearFreqsAndData();
long[] actualFreqs = mReader.readFreqs(mPowerProfile);
assertArrayEquals(freqs, actualFreqs);
// Verify that a second call will only return deltas.
mCallback.clear();
final long[][] newTimes1 = increaseTime(times);
- writeToFile(freqsLine(freqs) + uidLines(mUids, newTimes1));
+ setFreqsAndData(freqs, newTimes1);
mReader.readDelta(mCallback);
for (int i = 0; i < mUids.length; ++i) {
mCallback.verify(mUids[i], subtract(newTimes1[i], times[i]));
@@ -177,7 +191,7 @@
// Verify that calling with a null callback doesn't result in any crashes
mCallback.clear();
final long[][] newTimes2 = increaseTime(newTimes1);
- writeToFile(freqsLine(freqs) + uidLines(mUids, newTimes2));
+ setFreqsAndData(freqs, newTimes2);
mReader.readDelta(null);
mCallback.verifyNoMoreInteractions();
@@ -185,13 +199,13 @@
// the previous call had null callback.
mCallback.clear();
final long[][] newTimes3 = increaseTime(newTimes2);
- writeToFile(freqsLine(freqs) + uidLines(mUids, newTimes3));
+ setFreqsAndData(freqs, newTimes3);
mReader.readDelta(mCallback);
for (int i = 0; i < mUids.length; ++i) {
mCallback.verify(mUids[i], subtract(newTimes3[i], newTimes2[i]));
}
mCallback.verifyNoMoreInteractions();
- assertTrue(mTestFile.delete());
+ clearFreqsAndData();
}
@Test
@@ -199,7 +213,7 @@
final long[] freqs = {110, 123, 145, 167, 289, 997};
final long[][] times1 = increaseTime(new long[mUids.length][freqs.length]);
- writeToFile(freqsLine(freqs) + uidLines(mUids, times1));
+ setFreqsAndData(freqs, times1);
mReader.readAbsolute(mCallback);
for (int i = 0; i < mUids.length; i++) {
mCallback.verify(mUids[i], times1[i]);
@@ -207,20 +221,20 @@
mCallback.verifyNoMoreInteractions();
// Verify that readDelta also reads the frequencies if not already available.
- assertTrue(mTestFile.delete());
+ clearFreqsAndData();
long[] actualFreqs = mReader.readFreqs(mPowerProfile);
assertArrayEquals(freqs, actualFreqs);
// Verify that a second call should still return absolute values
mCallback.clear();
final long[][] times2 = increaseTime(times1);
- writeToFile(freqsLine(freqs) + uidLines(mUids, times2));
+ setFreqsAndData(freqs, times2);
mReader.readAbsolute(mCallback);
for (int i = 0; i < mUids.length; i++) {
mCallback.verify(mUids[i], times2[i]);
}
mCallback.verifyNoMoreInteractions();
- assertTrue(mTestFile.delete());
+ clearFreqsAndData();
}
@Test
@@ -228,14 +242,14 @@
final long[] freqs = {110, 123, 145, 167, 289, 997};
final long[][] times1 = increaseTime(new long[mUids.length][freqs.length]);
- writeToFile(freqsLine(freqs) + uidLines(mUids, times1));
+ setFreqsAndData(freqs, times1);
mReader.readDelta(mCallback);
// Verify that there should not be a callback for a particular UID if its time decreases.
mCallback.clear();
final long[][] times2 = increaseTime(times1);
times2[0][0] = 1000;
- writeToFile(freqsLine(freqs) + uidLines(mUids, times2));
+ setFreqsAndData(freqs, times2);
mReader.readDelta(mCallback);
for (int i = 1; i < mUids.length; i++) {
mCallback.verify(mUids[i], subtract(times2[i], times1[i]));
@@ -246,7 +260,7 @@
mCallback.clear();
final long[][] times3 = increaseTime(times2);
times3[0] = increaseTime(times1)[0];
- writeToFile(freqsLine(freqs) + uidLines(mUids, times3));
+ setFreqsAndData(freqs, times3);
mReader.readDelta(mCallback);
mCallback.verify(mUids[0], subtract(times3[0], times1[0]));
for (int i = 1; i < mUids.length; i++) {
@@ -258,7 +272,7 @@
mCallback.clear();
final long[][] times4 = increaseTime(times3);
times4[0][0] *= -1;
- writeToFile(freqsLine(freqs) + uidLines(mUids, times4));
+ setFreqsAndData(freqs, times4);
mReader.readDelta(mCallback);
for (int i = 1; i < mUids.length; ++i) {
mCallback.verify(mUids[i], subtract(times4[i], times3[i]));
@@ -269,14 +283,44 @@
mCallback.clear();
final long[][] times5 = increaseTime(times4);
times5[0] = increaseTime(times3)[0];
- writeToFile(freqsLine(freqs) + uidLines(mUids, times5));
+ setFreqsAndData(freqs, times5);
mReader.readDelta(mCallback);
mCallback.verify(mUids[0], subtract(times5[0], times3[0]));
for (int i = 1; i < mUids.length; i++) {
mCallback.verify(mUids[i], subtract(times5[i], times4[i]));
}
- assertTrue(mTestFile.delete());
+ clearFreqsAndData();
+ }
+
+ private void setFreqs(long[] freqs) throws IOException {
+ if (mUseBpf) {
+ mBpfMapReader.setFreqs(freqs);
+ } else {
+ writeToFile(freqsLine(freqs));
+ }
+ }
+
+ private void setFreqsAndData(long[] freqs, long[][] times) throws IOException {
+ if (mUseBpf) {
+ mBpfMapReader.setFreqs(freqs);
+ SparseArray<long[]> data = new SparseArray<>();
+ for (int i = 0; i < mUids.length; i++) {
+ data.put(mUids[i], times[i]);
+ }
+ mBpfMapReader.setData(data);
+ } else {
+ writeToFile(freqsLine(freqs) + uidLines(mUids, times));
+ }
+ }
+
+ private void clearFreqsAndData() {
+ if (mUseBpf) {
+ mBpfMapReader.setFreqs(null);
+ mBpfMapReader.setData(new SparseArray<>());
+ } else {
+ assertTrue(mTestFile.delete());
+ }
}
private String freqsLine(long[] freqs) {
@@ -358,4 +402,36 @@
assertEquals(0, mData.size());
}
}
+
+ private class KernelCpuUidTestBpfMapReader extends KernelCpuUidBpfMapReader {
+ private long[] mCpuFreqs;
+ private SparseArray<long[]> mNewData = new SparseArray<>();
+
+ public void setData(SparseArray<long[]> data) {
+ mNewData = data;
+ }
+
+ public void setFreqs(long[] freqs) {
+ mCpuFreqs = freqs;
+ }
+
+ @Override
+ public final boolean startTrackingBpfTimes() {
+ return true;
+ }
+
+ @Override
+ protected final boolean readBpfData() {
+ if (!mUseBpf) {
+ return false;
+ }
+ mData = mNewData;
+ return true;
+ }
+
+ @Override
+ public final long[] getDataDimensions() {
+ return mCpuFreqs;
+ }
+ }
}
diff --git a/core/tests/coretests/src/com/android/internal/os/KernelSingleUidTimeReaderTest.java b/core/tests/coretests/src/com/android/internal/os/KernelSingleUidTimeReaderTest.java
index 479e19e..dac35e5 100644
--- a/core/tests/coretests/src/com/android/internal/os/KernelSingleUidTimeReaderTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/KernelSingleUidTimeReaderTest.java
@@ -31,20 +31,33 @@
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameters;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.Arrays;
+import java.util.Collection;
@SmallTest
-@RunWith(AndroidJUnit4.class)
+@RunWith(Parameterized.class)
public class KernelSingleUidTimeReaderTest {
private final static int TEST_UID = 2222;
private final static int TEST_FREQ_COUNT = 5;
private KernelSingleUidTimeReader mReader;
private TestInjector mInjector;
+ protected boolean mUseBpf;
+
+ @Parameters(name="useBpf={0}")
+ public static Collection<Object[]> data() {
+ return Arrays.asList(new Object[][] { {true}, {false} });
+ }
+
+ public KernelSingleUidTimeReaderTest(boolean useBpf) {
+ mUseBpf = useBpf;
+ }
@Before
public void setUp() {
@@ -273,6 +286,7 @@
class TestInjector extends Injector {
private byte[] mData;
+ private long[] mBpfData;
private boolean mThrowExcpetion;
@Override
@@ -284,6 +298,14 @@
}
}
+ @Override
+ public long[] readBpfData(int uid) {
+ if (!mUseBpf || mBpfData == null) {
+ return new long[0];
+ }
+ return mBpfData;
+ }
+
public void setData(long[] cpuTimes) {
final ByteBuffer buffer = ByteBuffer.allocate(cpuTimes.length * Long.BYTES);
buffer.order(ByteOrder.nativeOrder());
@@ -291,6 +313,7 @@
buffer.putLong(time / 10);
}
mData = buffer.array();
+ mBpfData = cpuTimes.clone();
}
public void letReadDataThrowException(boolean throwException) {
diff --git a/core/tests/systemproperties/src/android/os/PropertyInvalidatedCacheTest.java b/core/tests/systemproperties/src/android/os/PropertyInvalidatedCacheTest.java
new file mode 100644
index 0000000..c4080e8
--- /dev/null
+++ b/core/tests/systemproperties/src/android/os/PropertyInvalidatedCacheTest.java
@@ -0,0 +1,168 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+import android.app.PropertyInvalidatedCache;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import junit.framework.TestCase;
+
+public class PropertyInvalidatedCacheTest extends TestCase {
+ private static final String KEY = "sys.testkey";
+ private static final String UNSET_KEY = "Aiw7woh6ie4toh7W";
+
+ private static class TestCache extends PropertyInvalidatedCache<Integer, String> {
+ TestCache() {
+ this(KEY);
+ }
+
+ TestCache(String key) {
+ super(4, key);
+ }
+
+ @Override
+ protected String recompute(Integer qv) {
+ mRecomputeCount += 1;
+ return "foo" + qv.toString();
+ }
+
+ int getRecomputeCount() {
+ return mRecomputeCount;
+ }
+
+ private int mRecomputeCount = 0;
+ }
+
+ @Override
+ protected void setUp() {
+ SystemProperties.set(KEY, "");
+ }
+
+ @SmallTest
+ public void testCacheRecompute() throws Exception {
+ TestCache cache = new TestCache();
+ cache.invalidateCache();
+ assertEquals("foo5", cache.query(5));
+ assertEquals(1, cache.getRecomputeCount());
+ assertEquals("foo5", cache.query(5));
+ assertEquals(1, cache.getRecomputeCount());
+ assertEquals("foo6", cache.query(6));
+ assertEquals(2, cache.getRecomputeCount());
+ cache.invalidateCache();
+ assertEquals("foo5", cache.query(5));
+ assertEquals("foo5", cache.query(5));
+ assertEquals(3, cache.getRecomputeCount());
+ }
+
+ @SmallTest
+ public void testCacheInitialState() throws Exception {
+ TestCache cache = new TestCache();
+ assertEquals("foo5", cache.query(5));
+ assertEquals("foo5", cache.query(5));
+ assertEquals(2, cache.getRecomputeCount());
+ cache.invalidateCache();
+ assertEquals("foo5", cache.query(5));
+ assertEquals("foo5", cache.query(5));
+ assertEquals(3, cache.getRecomputeCount());
+ }
+
+ @SmallTest
+ public void testCachePropertyUnset() throws Exception {
+ TestCache cache = new TestCache(UNSET_KEY);
+ assertEquals("foo5", cache.query(5));
+ assertEquals("foo5", cache.query(5));
+ assertEquals(2, cache.getRecomputeCount());
+ }
+
+ @SmallTest
+ public void testCacheDisableState() throws Exception {
+ TestCache cache = new TestCache();
+ assertEquals("foo5", cache.query(5));
+ assertEquals("foo5", cache.query(5));
+ assertEquals(2, cache.getRecomputeCount());
+ cache.invalidateCache();
+ assertEquals("foo5", cache.query(5));
+ assertEquals("foo5", cache.query(5));
+ assertEquals(3, cache.getRecomputeCount());
+ cache.disableSystemWide();
+ assertEquals("foo5", cache.query(5));
+ assertEquals("foo5", cache.query(5));
+ assertEquals(5, cache.getRecomputeCount());
+ cache.invalidateCache(); // Should not reenable
+ assertEquals("foo5", cache.query(5));
+ assertEquals("foo5", cache.query(5));
+ assertEquals(7, cache.getRecomputeCount());
+ }
+
+ @SmallTest
+ public void testRefreshSameObject() throws Exception {
+ int[] refreshCount = new int[1];
+ TestCache cache = new TestCache() {
+ @Override
+ protected String refresh(String oldResult, Integer query) {
+ refreshCount[0] += 1;
+ return oldResult;
+ }
+ };
+ cache.invalidateCache();
+ String result1 = cache.query(5);
+ assertEquals("foo5", result1);
+ String result2 = cache.query(5);
+ assertSame(result1, result2);
+ assertEquals(1, cache.getRecomputeCount());
+ assertEquals(1, refreshCount[0]);
+ assertEquals("foo5", cache.query(5));
+ assertEquals(2, refreshCount[0]);
+ }
+
+ @SmallTest
+ public void testRefreshInvalidateRace() throws Exception {
+ int[] refreshCount = new int[1];
+ TestCache cache = new TestCache() {
+ @Override
+ protected String refresh(String oldResult, Integer query) {
+ refreshCount[0] += 1;
+ invalidateCache();
+ return new String(oldResult);
+ }
+ };
+ cache.invalidateCache();
+ String result1 = cache.query(5);
+ assertEquals("foo5", result1);
+ String result2 = cache.query(5);
+ assertEquals(result1, result2);
+ assertNotSame(result1, result2);
+ assertEquals(2, cache.getRecomputeCount());
+ }
+
+ @SmallTest
+ public void testLocalProcessDisable() throws Exception {
+ TestCache cache = new TestCache();
+ cache.invalidateCache();
+ assertEquals("foo5", cache.query(5));
+ assertEquals(1, cache.getRecomputeCount());
+ assertEquals("foo5", cache.query(5));
+ assertEquals(1, cache.getRecomputeCount());
+ assertEquals(cache.isDisabledLocal(), false);
+ cache.disableLocal();
+ assertEquals(cache.isDisabledLocal(), true);
+ assertEquals("foo5", cache.query(5));
+ assertEquals("foo5", cache.query(5));
+ assertEquals(3, cache.getRecomputeCount());
+ }
+
+}
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index 821909d..162dcc9 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -26,6 +26,10 @@
<permission name="android.permission.REQUEST_NOTIFICATION_ASSISTANT_SERVICE" />
</privapp-permissions>
+ <privapp-permissions package="com.android.angle">
+ <permission name="android.permission.WRITE_SECURE_SETTINGS"/>
+ </privapp-permissions>
+
<privapp-permissions package="com.android.apps.tag">
<permission name="android.permission.WRITE_SECURE_SETTINGS"/>
</privapp-permissions>
@@ -377,6 +381,7 @@
<privapp-permissions package="com.android.dynsystem">
<permission name="android.permission.REBOOT"/>
<permission name="android.permission.MANAGE_DYNAMIC_SYSTEM"/>
+ <permission name="android.permission.READ_OEM_UNLOCK_STATE"/>
</privapp-permissions>
<privapp-permissions package="com.android.settings">
diff --git a/libs/androidfw/tests/CommonHelpers.h b/libs/androidfw/tests/CommonHelpers.h
index 8af13f2..316a57a 100644
--- a/libs/androidfw/tests/CommonHelpers.h
+++ b/libs/androidfw/tests/CommonHelpers.h
@@ -21,8 +21,6 @@
#include <string>
#include "androidfw/ResourceTypes.h"
-#include "utils/String16.h"
-#include "utils/String8.h"
namespace android {
@@ -40,10 +38,6 @@
return a.compare(b) == 0;
}
-static inline ::std::ostream& operator<<(::std::ostream& out, const String8& str) {
- return out << str.string();
-}
-
static inline ::std::ostream& operator<<(::std::ostream& out, const ResTable_config& c) {
return out << c.toString();
}
diff --git a/libs/usb/Android.bp b/libs/usb/Android.bp
index 027a748..e752b55 100644
--- a/libs/usb/Android.bp
+++ b/libs/usb/Android.bp
@@ -19,5 +19,3 @@
srcs: ["src/**/*.java"],
api_packages: ["com.android.future.usb"],
}
-
-subdirs = ["tests/*"]
diff --git a/libs/usb/tests/AccessoryChat/Android.bp b/libs/usb/tests/AccessoryChat/Android.bp
index 63a670c..19ed3d3 100644
--- a/libs/usb/tests/AccessoryChat/Android.bp
+++ b/libs/usb/tests/AccessoryChat/Android.bp
@@ -1,4 +1,3 @@
-subdirs = ["accessorychat"]
//
// Copyright (C) 2011 The Android Open Source Project
//
diff --git a/media/java/android/media/RingtoneManager.java b/media/java/android/media/RingtoneManager.java
index 6fd47c4..66a59e0 100644
--- a/media/java/android/media/RingtoneManager.java
+++ b/media/java/android/media/RingtoneManager.java
@@ -509,15 +509,19 @@
* @return The position of the {@link Uri}, or -1 if it cannot be found.
*/
public int getRingtonePosition(Uri ringtoneUri) {
- if (ringtoneUri == null) return -1;
- final long ringtoneId = ContentUris.parseId(ringtoneUri);
-
- final Cursor cursor = getCursor();
- cursor.moveToPosition(-1);
- while (cursor.moveToNext()) {
- if (ringtoneId == cursor.getLong(ID_COLUMN_INDEX)) {
- return cursor.getPosition();
+ try {
+ if (ringtoneUri == null) return -1;
+ final long ringtoneId = ContentUris.parseId(ringtoneUri);
+
+ final Cursor cursor = getCursor();
+ cursor.moveToPosition(-1);
+ while (cursor.moveToNext()) {
+ if (ringtoneId == cursor.getLong(ID_COLUMN_INDEX)) {
+ return cursor.getPosition();
+ }
}
+ } catch (NumberFormatException e) {
+ Log.e(TAG, "NumberFormatException while getting ringtone position, returning -1", e);
}
return -1;
}
diff --git a/media/java/android/media/tv/TvTrackInfo.java b/media/java/android/media/tv/TvTrackInfo.java
index d4c4a62..7d19985 100644
--- a/media/java/android/media/tv/TvTrackInfo.java
+++ b/media/java/android/media/tv/TvTrackInfo.java
@@ -18,6 +18,7 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
@@ -58,6 +59,8 @@
private final String mId;
private final String mLanguage;
private final CharSequence mDescription;
+ @Nullable
+ private final String mEncoding;
private final boolean mEncrypted;
private final int mAudioChannelCount;
private final int mAudioSampleRate;
@@ -73,14 +76,15 @@
private final Bundle mExtra;
private TvTrackInfo(int type, String id, String language, CharSequence description,
- boolean encrypted, int audioChannelCount, int audioSampleRate, boolean audioDescription,
- boolean hardOfHearing, boolean spokenSubtitle, int videoWidth, int videoHeight,
- float videoFrameRate, float videoPixelAspectRatio, byte videoActiveFormatDescription,
- Bundle extra) {
+ String encoding, boolean encrypted, int audioChannelCount, int audioSampleRate,
+ boolean audioDescription, boolean hardOfHearing, boolean spokenSubtitle, int videoWidth,
+ int videoHeight, float videoFrameRate, float videoPixelAspectRatio,
+ byte videoActiveFormatDescription, Bundle extra) {
mType = type;
mId = id;
mLanguage = language;
mDescription = description;
+ mEncoding = encoding;
mEncrypted = encrypted;
mAudioChannelCount = audioChannelCount;
mAudioSampleRate = audioSampleRate;
@@ -100,6 +104,7 @@
mId = in.readString();
mLanguage = in.readString();
mDescription = in.readString();
+ mEncoding = in.readString();
mEncrypted = in.readInt() != 0;
mAudioChannelCount = in.readInt();
mAudioSampleRate = in.readInt();
@@ -146,13 +151,26 @@
}
/**
+ * Returns the codec in the form of mime type. If the encoding is unknown or could not be
+ * determined, the corresponding value will be {@code null}.
+ *
+ * <p>For example of broadcast, codec information may be referred to broadcast standard (e.g.
+ * Component Descriptor of ETSI EN 300 468). In the case that track type is subtitle, mime type
+ * could be defined in broadcast standard (e.g. "text/dvb.subtitle" or "text/dvb.teletext" in
+ * ETSI TS 102 812 V1.3.1 section 7.6).
+ */
+ @Nullable
+ public String getEncoding() {
+ return mEncoding;
+ }
+
+ /**
* Returns {@code true} if the track is encrypted, {@code false} otherwise. If the encryption
* status is unknown or could not be determined, the corresponding value will be {@code false}.
*
* <p>For example: ISO/IEC 13818-1 defines a CA descriptor that can be used to determine the
* encryption status of some broadcast streams.
*/
-
public boolean isEncrypted() {
return mEncrypted;
}
@@ -323,6 +341,7 @@
dest.writeString(mId);
dest.writeString(mLanguage);
dest.writeString(mDescription != null ? mDescription.toString() : null);
+ dest.writeString(mEncoding);
dest.writeInt(mEncrypted ? 1 : 0);
dest.writeInt(mAudioChannelCount);
dest.writeInt(mAudioSampleRate);
@@ -352,6 +371,7 @@
if (!TextUtils.equals(mId, obj.mId) || mType != obj.mType
|| !TextUtils.equals(mLanguage, obj.mLanguage)
|| !TextUtils.equals(mDescription, obj.mDescription)
+ || !TextUtils.equals(mEncoding, obj.mEncoding)
|| mEncrypted != obj.mEncrypted) {
return false;
}
@@ -413,6 +433,7 @@
private final int mType;
private String mLanguage;
private CharSequence mDescription;
+ private String mEncoding;
private boolean mEncrypted;
private int mAudioChannelCount;
private int mAudioSampleRate;
@@ -468,6 +489,22 @@
}
/**
+ * Sets the encoding of the track.
+ *
+ * <p>For example of broadcast, codec information may be referred to broadcast standard
+ * (e.g. Component Descriptor of ETSI EN 300 468). In the case that track type is subtitle,
+ * mime type could be defined in broadcast standard (e.g. "text/dvb.subtitle" or
+ * "text/dvb.teletext" in ETSI TS 102 812 V1.3.1 section 7.6).
+ *
+ * @param encoding The encoding of the track in the form of mime type.
+ */
+ @NonNull
+ public Builder setEncoding(@Nullable String encoding) {
+ mEncoding = encoding;
+ return this;
+ }
+
+ /**
* Sets the encryption status of the track.
*
* <p>For example: ISO/IEC 13818-1 defines a CA descriptor that can be used to determine the
@@ -672,7 +709,7 @@
* @return The new {@link TvTrackInfo} instance
*/
public TvTrackInfo build() {
- return new TvTrackInfo(mType, mId, mLanguage, mDescription, mEncrypted,
+ return new TvTrackInfo(mType, mId, mLanguage, mDescription, mEncoding, mEncrypted,
mAudioChannelCount, mAudioSampleRate, mAudioDescription, mHardOfHearing,
mSpokenSubtitle, mVideoWidth, mVideoHeight, mVideoFrameRate,
mVideoPixelAspectRatio, mVideoActiveFormatDescription, mExtra);
diff --git a/media/jni/Android.bp b/media/jni/Android.bp
index f873eeb..27660db 100644
--- a/media/jni/Android.bp
+++ b/media/jni/Android.bp
@@ -124,8 +124,3 @@
"-Wunreachable-code",
],
}
-
-subdirs = [
- "audioeffect",
- "soundpool",
-]
diff --git a/media/native/Android.bp b/media/native/Android.bp
deleted file mode 100644
index b44c296..0000000
--- a/media/native/Android.bp
+++ /dev/null
@@ -1 +0,0 @@
-subdirs = ["*"]
diff --git a/packages/DynamicSystemInstallationService/AndroidManifest.xml b/packages/DynamicSystemInstallationService/AndroidManifest.xml
index d718eae9..b4d520d 100644
--- a/packages/DynamicSystemInstallationService/AndroidManifest.xml
+++ b/packages/DynamicSystemInstallationService/AndroidManifest.xml
@@ -7,6 +7,7 @@
<uses-permission android:name="android.permission.MANAGE_DYNAMIC_SYSTEM" />
<uses-permission android:name="android.permission.REBOOT" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
+ <uses-permission android:name="android.permission.READ_OEM_UNLOCK_STATE" />
<application
android:allowBackup="false"
diff --git a/packages/DynamicSystemInstallationService/res/values/strings.xml b/packages/DynamicSystemInstallationService/res/values/strings.xml
index 7595d2b..e124be6 100644
--- a/packages/DynamicSystemInstallationService/res/values/strings.xml
+++ b/packages/DynamicSystemInstallationService/res/values/strings.xml
@@ -18,6 +18,8 @@
<string name="notification_install_inprogress">Install in progress</string>
<!-- Displayed on notification: Dynamic System installation failed [CHAR LIMIT=128] -->
<string name="notification_install_failed">Install failed</string>
+ <!-- Displayed on notification: Image validation failed [CHAR LIMIT=128] -->
+ <string name="notification_image_validation_failed">Image validation failed. Abort installation.</string>
<!-- Displayed on notification: We are running in Dynamic System [CHAR LIMIT=128] -->
<string name="notification_dynsystem_in_use">Currently running a dynamic system. Restart to use the original Android version.</string>
@@ -25,10 +27,11 @@
<string name="notification_action_cancel">Cancel</string>
<!-- Action on notification: Discard installation [CHAR LIMIT=16] -->
<string name="notification_action_discard">Discard</string>
- <!-- Action on notification: Uninstall Dynamic System [CHAR LIMIT=16] -->
- <string name="notification_action_uninstall">Uninstall</string>
<!-- Action on notification: Restart to Dynamic System [CHAR LIMIT=16] -->
<string name="notification_action_reboot_to_dynsystem">Restart</string>
+ <!-- Action on notification: Restart to original Android version [CHAR LIMIT=16] -->
+ <string name="notification_action_reboot_to_origin">Restart</string>
+
<!-- Toast when installed Dynamic System is discarded [CHAR LIMIT=64] -->
<string name="toast_dynsystem_discarded">Discarded dynamic system</string>
diff --git a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java
index 9bae223..f36f97d 100644
--- a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java
+++ b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java
@@ -80,6 +80,10 @@
static final String KEY_ENABLE_WHEN_COMPLETED = "KEY_ENABLE_WHEN_COMPLETED";
static final String KEY_DSU_SLOT = "KEY_DSU_SLOT";
static final String DEFAULT_DSU_SLOT = "dsu";
+ static final String KEY_PUBKEY = "KEY_PUBKEY";
+
+ // Default userdata partition size is 2GiB.
+ private static final long DEFAULT_USERDATA_SIZE = 2L << 30;
/*
* Intent actions
@@ -267,6 +271,11 @@
long userdataSize = intent.getLongExtra(DynamicSystemClient.KEY_USERDATA_SIZE, 0);
mEnableWhenCompleted = intent.getBooleanExtra(KEY_ENABLE_WHEN_COMPLETED, false);
String dsuSlot = intent.getStringExtra(KEY_DSU_SLOT);
+ String publicKey = intent.getStringExtra(KEY_PUBKEY);
+
+ if (userdataSize == 0) {
+ userdataSize = DEFAULT_USERDATA_SIZE;
+ }
if (TextUtils.isEmpty(dsuSlot)) {
dsuSlot = DEFAULT_DSU_SLOT;
@@ -274,7 +283,7 @@
// TODO: better constructor or builder
mInstallTask =
new InstallationAsyncTask(
- url, dsuSlot, systemSize, userdataSize, this, mDynSystem, this);
+ url, dsuSlot, publicKey, systemSize, userdataSize, this, mDynSystem, this);
mInstallTask.execute();
@@ -408,6 +417,10 @@
}
private Notification buildNotification(int status, int cause) {
+ return buildNotification(status, cause, null);
+ }
+
+ private Notification buildNotification(int status, int cause, Throwable detail) {
Notification.Builder builder = new Notification.Builder(this, NOTIFICATION_CHANNEL_ID)
.setSmallIcon(R.drawable.ic_system_update_googblue_24dp)
.setProgress(0, 0, false);
@@ -456,14 +469,19 @@
.setStyle(new Notification.BigTextStyle().bigText(msgInUse));
builder.addAction(new Notification.Action.Builder(
- null, getString(R.string.notification_action_uninstall),
+ null, getString(R.string.notification_action_reboot_to_origin),
createPendingIntent(ACTION_REBOOT_TO_NORMAL)).build());
break;
case STATUS_NOT_STARTED:
if (cause != CAUSE_NOT_SPECIFIED && cause != CAUSE_INSTALL_CANCELLED) {
- builder.setContentText(getString(R.string.notification_install_failed));
+ if (detail instanceof InstallationAsyncTask.ImageValidationException) {
+ builder.setContentText(
+ getString(R.string.notification_image_validation_failed));
+ } else {
+ builder.setContentText(getString(R.string.notification_install_failed));
+ }
} else {
// no need to notify the user if the task is not started, or cancelled.
}
@@ -525,7 +543,7 @@
break;
}
- Log.d(TAG, "status=" + statusString + ", cause=" + causeString);
+ Log.d(TAG, "status=" + statusString + ", cause=" + causeString + ", detail=" + detail);
boolean notifyOnNotificationBar = true;
@@ -538,7 +556,7 @@
}
if (notifyOnNotificationBar) {
- mNM.notify(NOTIFICATION_ID, buildNotification(status, cause));
+ mNM.notify(NOTIFICATION_ID, buildNotification(status, cause, detail));
}
for (int i = mClients.size() - 1; i >= 0; i--) {
diff --git a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/InstallationAsyncTask.java b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/InstallationAsyncTask.java
index 438c435..f8952ac 100644
--- a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/InstallationAsyncTask.java
+++ b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/InstallationAsyncTask.java
@@ -17,11 +17,13 @@
package com.android.dynsystem;
import android.content.Context;
+import android.gsi.AvbPublicKey;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.MemoryFile;
import android.os.ParcelFileDescriptor;
import android.os.image.DynamicSystemManager;
+import android.service.persistentdata.PersistentDataBlockManager;
import android.util.Log;
import android.webkit.URLUtil;
@@ -51,18 +53,46 @@
private static final List<String> UNSUPPORTED_PARTITIONS =
Arrays.asList("vbmeta", "boot", "userdata", "dtbo", "super_empty", "system_other");
- private class UnsupportedUrlException extends RuntimeException {
+ private class UnsupportedUrlException extends Exception {
private UnsupportedUrlException(String message) {
super(message);
}
}
- private class UnsupportedFormatException extends RuntimeException {
+ private class UnsupportedFormatException extends Exception {
private UnsupportedFormatException(String message) {
super(message);
}
}
+ static class ImageValidationException extends Exception {
+ ImageValidationException(String message) {
+ super(message);
+ }
+
+ ImageValidationException(Throwable cause) {
+ super(cause);
+ }
+ }
+
+ static class RevocationListFetchException extends ImageValidationException {
+ RevocationListFetchException(Throwable cause) {
+ super(cause);
+ }
+ }
+
+ static class KeyRevokedException extends ImageValidationException {
+ KeyRevokedException(String message) {
+ super(message);
+ }
+ }
+
+ static class PublicKeyException extends ImageValidationException {
+ PublicKeyException(String message) {
+ super(message);
+ }
+ }
+
/** UNSET means the installation is not completed */
static final int RESULT_UNSET = 0;
static final int RESULT_OK = 1;
@@ -97,12 +127,14 @@
private final String mUrl;
private final String mDsuSlot;
+ private final String mPublicKey;
private final long mSystemSize;
private final long mUserdataSize;
private final Context mContext;
private final DynamicSystemManager mDynSystem;
private final ProgressListener mListener;
private final boolean mIsNetworkUrl;
+ private final boolean mIsDeviceBootloaderUnlocked;
private DynamicSystemManager.Session mInstallationSession;
private KeyRevocationList mKeyRevocationList;
@@ -115,6 +147,7 @@
InstallationAsyncTask(
String url,
String dsuSlot,
+ String publicKey,
long systemSize,
long userdataSize,
Context context,
@@ -122,12 +155,20 @@
ProgressListener listener) {
mUrl = url;
mDsuSlot = dsuSlot;
+ mPublicKey = publicKey;
mSystemSize = systemSize;
mUserdataSize = userdataSize;
mContext = context;
mDynSystem = dynSystem;
mListener = listener;
mIsNetworkUrl = URLUtil.isNetworkUrl(mUrl);
+ PersistentDataBlockManager pdbManager =
+ (PersistentDataBlockManager)
+ mContext.getSystemService(Context.PERSISTENT_DATA_BLOCK_SERVICE);
+ mIsDeviceBootloaderUnlocked =
+ (pdbManager != null)
+ && (pdbManager.getFlashLockState()
+ == PersistentDataBlockManager.FLASH_LOCK_UNLOCKED);
}
@Override
@@ -157,8 +198,6 @@
return null;
}
- // TODO(yochiang): do post-install public key check (revocation list / boot-ramdisk)
-
mDynSystem.finishInstallation();
} catch (Exception e) {
Log.e(TAG, e.toString(), e);
@@ -242,23 +281,26 @@
String.format(Locale.US, "Unsupported URL: %s", mUrl));
}
- // TODO(yochiang): Bypass this check if device is unlocked
try {
String listUrl = mContext.getString(R.string.key_revocation_list_url);
mKeyRevocationList = KeyRevocationList.fromUrl(new URL(listUrl));
} catch (IOException | JSONException e) {
- Log.d(TAG, "Failed to fetch Dynamic System Key Revocation List");
mKeyRevocationList = new KeyRevocationList();
- keyRevocationThrowOrWarning(e);
+ imageValidationThrowOrWarning(new RevocationListFetchException(e));
+ }
+ if (mKeyRevocationList.isRevoked(mPublicKey)) {
+ imageValidationThrowOrWarning(new KeyRevokedException(mPublicKey));
}
}
- private void keyRevocationThrowOrWarning(Exception e) throws Exception {
- if (mIsNetworkUrl) {
- throw e;
- } else {
- // If DSU is being installed from a local file URI, then be permissive
+ private void imageValidationThrowOrWarning(ImageValidationException e)
+ throws ImageValidationException {
+ if (mIsDeviceBootloaderUnlocked || !mIsNetworkUrl) {
+ // If device is OEM unlocked or DSU is being installed from a local file URI,
+ // then be permissive.
Log.w(TAG, e.toString());
+ } else {
+ throw e;
}
}
@@ -294,7 +336,8 @@
}
}
- private void installImages() throws IOException, InterruptedException {
+ private void installImages()
+ throws IOException, InterruptedException, ImageValidationException {
if (mStream != null) {
if (mIsZip) {
installStreamingZipUpdate();
@@ -306,12 +349,14 @@
}
}
- private void installStreamingGzUpdate() throws IOException, InterruptedException {
+ private void installStreamingGzUpdate()
+ throws IOException, InterruptedException, ImageValidationException {
Log.d(TAG, "To install a streaming GZ update");
installImage("system", mSystemSize, new GZIPInputStream(mStream), 1);
}
- private void installStreamingZipUpdate() throws IOException, InterruptedException {
+ private void installStreamingZipUpdate()
+ throws IOException, InterruptedException, ImageValidationException {
Log.d(TAG, "To install a streaming ZIP update");
ZipInputStream zis = new ZipInputStream(mStream);
@@ -330,7 +375,8 @@
}
}
- private void installLocalZipUpdate() throws IOException, InterruptedException {
+ private void installLocalZipUpdate()
+ throws IOException, InterruptedException, ImageValidationException {
Log.d(TAG, "To install a local ZIP update");
Enumeration<? extends ZipEntry> entries = mZipFile.entries();
@@ -349,8 +395,9 @@
}
}
- private boolean installImageFromAnEntry(ZipEntry entry, InputStream is,
- int numInstalledPartitions) throws IOException, InterruptedException {
+ private boolean installImageFromAnEntry(
+ ZipEntry entry, InputStream is, int numInstalledPartitions)
+ throws IOException, InterruptedException, ImageValidationException {
String name = entry.getName();
Log.d(TAG, "ZipEntry: " + name);
@@ -373,8 +420,9 @@
return true;
}
- private void installImage(String partitionName, long uncompressedSize, InputStream is,
- int numInstalledPartitions) throws IOException, InterruptedException {
+ private void installImage(
+ String partitionName, long uncompressedSize, InputStream is, int numInstalledPartitions)
+ throws IOException, InterruptedException, ImageValidationException {
SparseInputStream sis = new SparseInputStream(new BufferedInputStream(is));
@@ -445,6 +493,24 @@
publishProgress(progress);
}
}
+
+ AvbPublicKey avbPublicKey = new AvbPublicKey();
+ if (!mInstallationSession.getAvbPublicKey(avbPublicKey)) {
+ imageValidationThrowOrWarning(new PublicKeyException("getAvbPublicKey() failed"));
+ } else {
+ String publicKey = toHexString(avbPublicKey.sha1);
+ if (mKeyRevocationList.isRevoked(publicKey)) {
+ imageValidationThrowOrWarning(new KeyRevokedException(publicKey));
+ }
+ }
+ }
+
+ private static String toHexString(byte[] bytes) {
+ StringBuilder sb = new StringBuilder();
+ for (byte b : bytes) {
+ sb.append(String.format("%02x", b));
+ }
+ return sb.toString();
}
private void close() {
diff --git a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/VerificationActivity.java b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/VerificationActivity.java
index e42ded7..82ea744 100644
--- a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/VerificationActivity.java
+++ b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/VerificationActivity.java
@@ -84,7 +84,6 @@
// retrieve data from calling intent
Intent callingIntent = getIntent();
Uri url = callingIntent.getData();
- Bundle extras = callingIntent.getExtras();
if (url != null) {
sVerifiedUrl = url.toString();
@@ -96,7 +95,7 @@
intent.setData(url);
}
intent.setAction(DynamicSystemClient.ACTION_START_INSTALL);
- intent.putExtras(extras);
+ intent.putExtras(callingIntent);
Log.d(TAG, "Starting Installation Service");
startServiceAsUser(intent, UserHandle.SYSTEM);
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index e75ad13..6c1d2796 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -1009,6 +1009,11 @@
String value = setting != null ? setting.getValue() : null;
updateGlobalSetting(Settings.Global.ADB_ENABLED,
value, null, true, userId, true);
+
+ setting = getGlobalSetting(Settings.Global.ADB_WIFI_ENABLED);
+ value = setting != null ? setting.getValue() : null;
+ updateGlobalSetting(Settings.Global.ADB_WIFI_ENABLED,
+ value, null, true, userId, true);
}
} finally {
Binder.restoreCallingIdentity(identity);
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 262365da..a9fa2ac 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -430,6 +430,25 @@
android:excludeFromRecents="true">
</activity>
+ <!-- started from WirelessDebuggingManager -->
+ <activity android:name=".wifi.WifiDebuggingActivity"
+ android:permission="android.permission.MANAGE_DEBUGGING"
+ android:theme="@style/Theme.SystemUI.Dialog.Alert"
+ android:finishOnCloseSystemDialogs="true"
+ android:excludeFromRecents="true">
+ </activity>
+ <activity-alias
+ android:name=".WifiDebuggingActivityAlias"
+ android:permission="android.permission.DUMP"
+ android:targetActivity=".wifi.WifiDebuggingActivity"
+ android:exported="true">
+ </activity-alias>
+ <activity android:name=".wifi.WifiDebuggingSecondaryUserActivity"
+ android:theme="@style/Theme.SystemUI.Dialog.Alert"
+ android:finishOnCloseSystemDialogs="true"
+ android:excludeFromRecents="true">
+ </activity>
+
<!-- started from NetworkPolicyManagerService -->
<activity
android:name=".net.NetworkOverLimitActivity"
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index d58f83f..f5ccc53 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -162,6 +162,24 @@
<!-- Message of notification shown when trying to enable USB debugging but a secondary user is the current foreground user. -->
<string name="usb_debugging_secondary_user_message">The user currently signed in to this device can\'t turn on USB debugging. To use this feature, switch to the primary user.</string>
+ <!-- Title of confirmation dialog for wireless debugging [CHAR LIMIT=NONE] -->
+ <string name="wifi_debugging_title">Allow wireless debugging on this network?</string>
+
+ <!-- Message of confirmation dialog for wireless debugging [CHAR LIMIT=NONE] -->
+ <string name="wifi_debugging_message">Network Name (SSID)\n<xliff:g id="ssid" example="My wifi">%1$s</xliff:g>\n\nWi\u2011Fi Address (BSSID)\n<xliff:g id="bssid" example="AB:CD:EF:12:34:56">%2$s</xliff:g></string>
+
+ <!-- Option to always allow wireless debugging on this network [CHAR LIMIT=NONE] -->
+ <string name="wifi_debugging_always">Always allow on this network</string>
+
+ <!-- Button label for confirming acceptance of enabling wireless debugging [CHAR LIMIT=15] -->
+ <string name="wifi_debugging_allow">Allow</string>
+
+ <!-- Title of notification shown when trying to enable wireless debugging but a secondary user is the current foreground user. [CHAR LIMIT=NONE] -->
+ <string name="wifi_debugging_secondary_user_title">Wireless debugging not allowed</string>
+
+ <!-- Message of notification shown when trying to enable wireless debugging but a secondary user is the current foreground user. [CHAR LIMIT=NONE] -->
+ <string name="wifi_debugging_secondary_user_message">The user currently signed in to this device can\u2019t turn on wireless debugging. To use this feature, switch to the primary user.</string>
+
<!-- Title of USB contaminant presence dialog [CHAR LIMIT=NONE] -->
<string name="usb_contaminant_title">USB port disabled</string>
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
index b73aff4..e2cafdf 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/MobileSignalController.java
@@ -26,6 +26,7 @@
import android.os.Looper;
import android.os.Message;
import android.provider.Settings.Global;
+import android.telephony.Annotation;
import android.telephony.CellSignalStrength;
import android.telephony.CellSignalStrengthCdma;
import android.telephony.NetworkRegistrationInfo;
@@ -38,7 +39,6 @@
import android.text.Html;
import android.text.TextUtils;
import android.util.Log;
-import android.util.SparseArray;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.telephony.TelephonyIntents;
@@ -54,7 +54,9 @@
import java.io.PrintWriter;
import java.util.BitSet;
+import java.util.HashMap;
import java.util.List;
+import java.util.Map;
import java.util.Objects;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -77,12 +79,14 @@
final SubscriptionInfo mSubscriptionInfo;
// @VisibleForDemoMode
- final SparseArray<MobileIconGroup> mNetworkToIconLookup;
+ final Map<String, MobileIconGroup> mNetworkToIconLookup;
// Since some pieces of the phone state are interdependent we store it locally,
// this could potentially become part of MobileState for simplification/complication
// of code.
private int mDataNetType = TelephonyManager.NETWORK_TYPE_UNKNOWN;
+ private boolean mCA = false;
+ private boolean mCAPlus = false;
private int mDataState = TelephonyManager.DATA_DISCONNECTED;
private ServiceState mServiceState;
private SignalStrength mSignalStrength;
@@ -93,9 +97,6 @@
boolean mInflateSignalStrengths = false;
@VisibleForTesting
boolean mIsShowingIconGracefully = false;
- // Some specific carriers have 5GE network which is special LTE CA network.
- private static final int NETWORK_TYPE_LTE_CA_5GE =
- TelephonyManager.getAllNetworkTypes().length + 1;
// TODO: Reduce number of vars passed in, if we have the NetworkController, probably don't
// need listener lists anymore.
@@ -106,7 +107,7 @@
super("MobileSignalController(" + info.getSubscriptionId() + ")", context,
NetworkCapabilities.TRANSPORT_CELLULAR, callbackHandler,
networkController);
- mNetworkToIconLookup = new SparseArray<>();
+ mNetworkToIconLookup = new HashMap<>();
mConfig = config;
mPhone = phone;
mDefaults = defaults;
@@ -210,29 +211,38 @@
private void mapIconSets() {
mNetworkToIconLookup.clear();
- mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_EVDO_0, TelephonyIcons.THREE_G);
- mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_EVDO_A, TelephonyIcons.THREE_G);
- mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_EVDO_B, TelephonyIcons.THREE_G);
- mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_EHRPD, TelephonyIcons.THREE_G);
- mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_UMTS, TelephonyIcons.THREE_G);
- mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_TD_SCDMA, TelephonyIcons.THREE_G);
+ mNetworkToIconLookup.put(toIconKey(TelephonyManager.NETWORK_TYPE_EVDO_0),
+ TelephonyIcons.THREE_G);
+ mNetworkToIconLookup.put(toIconKey(TelephonyManager.NETWORK_TYPE_EVDO_A),
+ TelephonyIcons.THREE_G);
+ mNetworkToIconLookup.put(toIconKey(TelephonyManager.NETWORK_TYPE_EVDO_B),
+ TelephonyIcons.THREE_G);
+ mNetworkToIconLookup.put(toIconKey(TelephonyManager.NETWORK_TYPE_EHRPD),
+ TelephonyIcons.THREE_G);
+ mNetworkToIconLookup.put(toIconKey(TelephonyManager.NETWORK_TYPE_UMTS),
+ TelephonyIcons.THREE_G);
+ mNetworkToIconLookup.put(toIconKey(TelephonyManager.NETWORK_TYPE_TD_SCDMA),
+ TelephonyIcons.THREE_G);
if (!mConfig.showAtLeast3G) {
- mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_UNKNOWN,
+ mNetworkToIconLookup.put(toIconKey(TelephonyManager.NETWORK_TYPE_UNKNOWN),
TelephonyIcons.UNKNOWN);
- mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_EDGE, TelephonyIcons.E);
- mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_CDMA, TelephonyIcons.ONE_X);
- mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_1xRTT, TelephonyIcons.ONE_X);
+ mNetworkToIconLookup.put(toIconKey(TelephonyManager.NETWORK_TYPE_EDGE),
+ TelephonyIcons.E);
+ mNetworkToIconLookup.put(toIconKey(TelephonyManager.NETWORK_TYPE_CDMA),
+ TelephonyIcons.ONE_X);
+ mNetworkToIconLookup.put(toIconKey(TelephonyManager.NETWORK_TYPE_1xRTT),
+ TelephonyIcons.ONE_X);
mDefaultIcons = TelephonyIcons.G;
} else {
- mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_UNKNOWN,
+ mNetworkToIconLookup.put(toIconKey(TelephonyManager.NETWORK_TYPE_UNKNOWN),
TelephonyIcons.THREE_G);
- mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_EDGE,
+ mNetworkToIconLookup.put(toIconKey(TelephonyManager.NETWORK_TYPE_EDGE),
TelephonyIcons.THREE_G);
- mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_CDMA,
+ mNetworkToIconLookup.put(toIconKey(TelephonyManager.NETWORK_TYPE_CDMA),
TelephonyIcons.THREE_G);
- mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_1xRTT,
+ mNetworkToIconLookup.put(toIconKey(TelephonyManager.NETWORK_TYPE_1xRTT),
TelephonyIcons.THREE_G);
mDefaultIcons = TelephonyIcons.THREE_G;
}
@@ -247,33 +257,59 @@
hPlusGroup = TelephonyIcons.H_PLUS;
}
- mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_HSDPA, hGroup);
- mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_HSUPA, hGroup);
- mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_HSPA, hGroup);
- mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_HSPAP, hPlusGroup);
+ mNetworkToIconLookup.put(toIconKey(TelephonyManager.NETWORK_TYPE_HSDPA), hGroup);
+ mNetworkToIconLookup.put(toIconKey(TelephonyManager.NETWORK_TYPE_HSUPA), hGroup);
+ mNetworkToIconLookup.put(toIconKey(TelephonyManager.NETWORK_TYPE_HSPA), hGroup);
+ mNetworkToIconLookup.put(toIconKey(TelephonyManager.NETWORK_TYPE_HSPAP), hPlusGroup);
if (mConfig.show4gForLte) {
- mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_LTE, TelephonyIcons.FOUR_G);
+ mNetworkToIconLookup.put(toIconKey(TelephonyManager.NETWORK_TYPE_LTE),
+ TelephonyIcons.FOUR_G);
if (mConfig.hideLtePlus) {
- mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_LTE_CA,
+ mNetworkToIconLookup.put(toIconKeyCA(TelephonyManager.NETWORK_TYPE_LTE),
TelephonyIcons.FOUR_G);
} else {
- mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_LTE_CA,
+ mNetworkToIconLookup.put(toIconKeyCA(TelephonyManager.NETWORK_TYPE_LTE),
TelephonyIcons.FOUR_G_PLUS);
}
} else {
- mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_LTE, TelephonyIcons.LTE);
+ mNetworkToIconLookup.put(toIconKey(TelephonyManager.NETWORK_TYPE_LTE),
+ TelephonyIcons.LTE);
if (mConfig.hideLtePlus) {
- mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_LTE_CA,
+ mNetworkToIconLookup.put(toIconKeyCA(TelephonyManager.NETWORK_TYPE_LTE),
TelephonyIcons.LTE);
} else {
- mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_LTE_CA,
+ mNetworkToIconLookup.put(toIconKeyCA(TelephonyManager.NETWORK_TYPE_LTE),
TelephonyIcons.LTE_PLUS);
}
}
- mNetworkToIconLookup.put(NETWORK_TYPE_LTE_CA_5GE,
+ mNetworkToIconLookup.put(toIconKeyCAPlus(TelephonyManager.NETWORK_TYPE_LTE),
TelephonyIcons.LTE_CA_5G_E);
- mNetworkToIconLookup.put(TelephonyManager.NETWORK_TYPE_IWLAN, TelephonyIcons.WFC);
+ mNetworkToIconLookup.put(toIconKey(TelephonyManager.NETWORK_TYPE_IWLAN),
+ TelephonyIcons.WFC);
+ }
+
+ private String getIconKey() {
+ if (mCA) {
+ return toIconKeyCA(mDataNetType);
+ } else if (mCAPlus) {
+ return toIconKeyCAPlus(mDataNetType);
+ } else {
+ return toIconKey(mDataNetType);
+ }
+ }
+
+ // Some specific carriers have 5GE network which is special CA network.
+ private String toIconKeyCAPlus(@Annotation.NetworkType int networkType) {
+ return toIconKeyCA(networkType) + "_Plus";
+ }
+
+ private String toIconKeyCA(@Annotation.NetworkType int networkType) {
+ return toIconKey(networkType) + "_CA";
+ }
+
+ private String toIconKey(@Annotation.NetworkType int networkType) {
+ return Integer.toString(networkType);
}
private void updateInflateSignalStrength() {
@@ -527,10 +563,11 @@
nr5GIconGroup = adjustNr5GIconGroupByDisplayGraceTime(nr5GIconGroup);
}
+ String iconKey = getIconKey();
if (nr5GIconGroup != null) {
mCurrentState.iconGroup = nr5GIconGroup;
- } else if (mNetworkToIconLookup.indexOfKey(mDataNetType) >= 0) {
- mCurrentState.iconGroup = mNetworkToIconLookup.get(mDataNetType);
+ } else if (mNetworkToIconLookup.get(iconKey) != null) {
+ mCurrentState.iconGroup = mNetworkToIconLookup.get(iconKey);
} else {
mCurrentState.iconGroup = mDefaultIcons;
}
@@ -683,6 +720,8 @@
pw.println(" mSignalStrength=" + mSignalStrength + ",");
pw.println(" mDataState=" + mDataState + ",");
pw.println(" mDataNetType=" + mDataNetType + ",");
+ pw.println(" mCA=" + mCA + ",");
+ pw.println(" mCAPlus=" + mCAPlus + ",");
pw.println(" mInflateSignalStrengths=" + mInflateSignalStrengths + ",");
pw.println(" isDataDisabled=" + isDataDisabled() + ",");
pw.println(" mIsShowingIconGracefully=" + mIsShowingIconGracefully + ",");
@@ -733,11 +772,13 @@
private void updateDataNetType(int networkType) {
mDataNetType = networkType;
+ mCA = false;
+ mCAPlus = false;
if (mDataNetType == TelephonyManager.NETWORK_TYPE_LTE) {
if (isCarrierSpecificDataIcon()) {
- mDataNetType = NETWORK_TYPE_LTE_CA_5GE;
+ mCAPlus = true;
} else if (mServiceState != null && mServiceState.isUsingCarrierAggregation()) {
- mDataNetType = TelephonyManager.NETWORK_TYPE_LTE_CA;
+ mCA = true;
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/wifi/WifiDebuggingActivity.java b/packages/SystemUI/src/com/android/systemui/wifi/WifiDebuggingActivity.java
new file mode 100644
index 0000000..a94af24
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/wifi/WifiDebuggingActivity.java
@@ -0,0 +1,223 @@
+/*
+ * Copyright (C) 2012 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.systemui.wifi;
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.debug.IAdbManager;
+import android.net.ConnectivityManager;
+import android.net.NetworkInfo;
+import android.net.wifi.WifiInfo;
+import android.net.wifi.WifiManager;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.ServiceManager;
+import android.util.EventLog;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.Window;
+import android.view.WindowManager;
+import android.widget.CheckBox;
+import android.widget.Toast;
+
+import com.android.internal.app.AlertActivity;
+import com.android.internal.app.AlertController;
+import com.android.systemui.R;
+
+/**
+ * Alerts the user of an untrusted network when enabling wireless debugging.
+ * The user can either deny, allow, or allow with the "always allow on this
+ * network" checked.
+ */
+public class WifiDebuggingActivity extends AlertActivity
+ implements DialogInterface.OnClickListener {
+ private static final String TAG = "WifiDebuggingActivity";
+
+ private CheckBox mAlwaysAllow;
+ // Notifies when wifi is disabled, or the network changed
+ private WifiChangeReceiver mWifiChangeReceiver;
+ private WifiManager mWifiManager;
+ private String mBssid;
+ private boolean mClicked = false;
+
+ @Override
+ public void onCreate(Bundle icicle) {
+ Window window = getWindow();
+ window.addSystemFlags(
+ WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS);
+ window.setType(WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG);
+
+ super.onCreate(icicle);
+
+
+ mWifiManager = (WifiManager) getSystemService(Context.WIFI_SERVICE);
+ mWifiChangeReceiver = new WifiChangeReceiver(this);
+
+ Intent intent = getIntent();
+ String ssid = intent.getStringExtra("ssid");
+ mBssid = intent.getStringExtra("bssid");
+
+ if (ssid == null || mBssid == null) {
+ finish();
+ return;
+ }
+
+ final AlertController.AlertParams ap = mAlertParams;
+ ap.mTitle = getString(R.string.wifi_debugging_title);
+ ap.mMessage = getString(R.string.wifi_debugging_message, ssid, mBssid);
+ ap.mPositiveButtonText = getString(R.string.wifi_debugging_allow);
+ ap.mNegativeButtonText = getString(android.R.string.cancel);
+ ap.mPositiveButtonListener = this;
+ ap.mNegativeButtonListener = this;
+
+ // add "always allow" checkbox
+ LayoutInflater inflater = LayoutInflater.from(ap.mContext);
+ View checkbox = inflater.inflate(com.android.internal.R.layout.always_use_checkbox, null);
+ mAlwaysAllow = (CheckBox) checkbox.findViewById(com.android.internal.R.id.alwaysUse);
+ mAlwaysAllow.setText(getString(R.string.wifi_debugging_always));
+ ap.mView = checkbox;
+ window.setCloseOnTouchOutside(false);
+
+ setupAlert();
+
+ // adding touch listener on affirmative button - checks if window is obscured
+ // if obscured, do not let user give permissions (could be tapjacking involved)
+ final View.OnTouchListener filterTouchListener = (View v, MotionEvent event) -> {
+ // Filter obscured touches by consuming them.
+ if (((event.getFlags() & MotionEvent.FLAG_WINDOW_IS_OBSCURED) != 0)
+ || ((event.getFlags() & MotionEvent.FLAG_WINDOW_IS_PARTIALLY_OBSCURED) != 0)) {
+ if (event.getAction() == MotionEvent.ACTION_UP) {
+ // TODO: need a different value for safety net?
+ EventLog.writeEvent(0x534e4554, "62187985"); // safety net logging
+ Toast.makeText(v.getContext(),
+ R.string.touch_filtered_warning,
+ Toast.LENGTH_SHORT).show();
+ }
+ return true;
+ }
+ return false;
+ };
+ mAlert.getButton(BUTTON_POSITIVE).setOnTouchListener(filterTouchListener);
+
+ }
+
+ @Override
+ public void onWindowAttributesChanged(WindowManager.LayoutParams params) {
+ super.onWindowAttributesChanged(params);
+ }
+
+ private class WifiChangeReceiver extends BroadcastReceiver {
+ private final Activity mActivity;
+ WifiChangeReceiver(Activity activity) {
+ mActivity = activity;
+ }
+
+ @Override
+ public void onReceive(Context content, Intent intent) {
+ String action = intent.getAction();
+ if (WifiManager.WIFI_STATE_CHANGED_ACTION.equals(action)) {
+ int state = intent.getIntExtra(
+ WifiManager.EXTRA_WIFI_STATE, WifiManager.WIFI_STATE_DISABLED);
+ if (state == WifiManager.WIFI_STATE_DISABLED) {
+ mActivity.finish();
+ }
+ } else if (WifiManager.NETWORK_STATE_CHANGED_ACTION.equals(action)) {
+ NetworkInfo networkInfo = (NetworkInfo) intent.getParcelableExtra(
+ WifiManager.EXTRA_NETWORK_INFO);
+ if (networkInfo.getType() == ConnectivityManager.TYPE_WIFI) {
+ if (!networkInfo.isConnected()) {
+ mActivity.finish();
+ return;
+ }
+ WifiInfo wifiInfo = mWifiManager.getConnectionInfo();
+ if (wifiInfo == null || wifiInfo.getNetworkId() == -1) {
+ mActivity.finish();
+ return;
+ }
+ String bssid = wifiInfo.getBSSID();
+ if (bssid == null || bssid.isEmpty()) {
+ mActivity.finish();
+ return;
+ }
+ if (!bssid.equals(mBssid)) {
+ mActivity.finish();
+ return;
+ }
+ }
+ }
+ }
+ }
+
+ @Override
+ public void onStart() {
+ super.onStart();
+ IntentFilter filter = new IntentFilter(WifiManager.WIFI_STATE_CHANGED_ACTION);
+ filter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
+ registerReceiver(mWifiChangeReceiver, filter);
+ }
+
+ @Override
+ protected void onStop() {
+ if (mWifiChangeReceiver != null) {
+ unregisterReceiver(mWifiChangeReceiver);
+ }
+ super.onStop();
+ }
+
+ @Override
+ protected void onDestroy() {
+ super.onDestroy();
+
+ // In the case where user dismissed the dialog, we don't get an onClick event.
+ // In that case, tell adb to deny the network connection.
+ if (!mClicked) {
+ try {
+ IBinder b = ServiceManager.getService(ADB_SERVICE);
+ IAdbManager service = IAdbManager.Stub.asInterface(b);
+ service.denyWirelessDebugging();
+ } catch (Exception e) {
+ Log.e(TAG, "Unable to notify Adb service", e);
+ }
+ }
+ }
+
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ mClicked = true;
+ boolean allow = (which == AlertDialog.BUTTON_POSITIVE);
+ boolean alwaysAllow = allow && mAlwaysAllow.isChecked();
+ try {
+ IBinder b = ServiceManager.getService(ADB_SERVICE);
+ IAdbManager service = IAdbManager.Stub.asInterface(b);
+ if (allow) {
+ service.allowWirelessDebugging(alwaysAllow, mBssid);
+ } else {
+ service.denyWirelessDebugging();
+ }
+ } catch (Exception e) {
+ Log.e(TAG, "Unable to notify Adb service", e);
+ }
+ finish();
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/wifi/WifiDebuggingSecondaryUserActivity.java b/packages/SystemUI/src/com/android/systemui/wifi/WifiDebuggingSecondaryUserActivity.java
new file mode 100644
index 0000000..0266a84
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/wifi/WifiDebuggingSecondaryUserActivity.java
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2015 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.systemui.wifi;
+
+import android.app.Activity;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.net.ConnectivityManager;
+import android.net.NetworkInfo;
+import android.net.wifi.WifiInfo;
+import android.net.wifi.WifiManager;
+import android.os.Bundle;
+
+import com.android.internal.app.AlertActivity;
+import com.android.internal.app.AlertController;
+import com.android.systemui.R;
+
+/**
+ * Alerts the user that wireless debugging cannot be enabled by a secondary user.
+ */
+public class WifiDebuggingSecondaryUserActivity extends AlertActivity
+ implements DialogInterface.OnClickListener {
+ private WifiChangeReceiver mWifiChangeReceiver;
+ private WifiManager mWifiManager;
+
+ @Override
+ public void onCreate(Bundle icicle) {
+ super.onCreate(icicle);
+
+ mWifiManager = (WifiManager) getSystemService(Context.WIFI_SERVICE);
+ mWifiChangeReceiver = new WifiChangeReceiver(this);
+
+ final AlertController.AlertParams ap = mAlertParams;
+ ap.mTitle = getString(R.string.wifi_debugging_secondary_user_title);
+ ap.mMessage = getString(R.string.wifi_debugging_secondary_user_message);
+ ap.mPositiveButtonText = getString(android.R.string.ok);
+ ap.mPositiveButtonListener = this;
+
+ setupAlert();
+ }
+
+ private class WifiChangeReceiver extends BroadcastReceiver {
+ private final Activity mActivity;
+ WifiChangeReceiver(Activity activity) {
+ mActivity = activity;
+ }
+
+ @Override
+ public void onReceive(Context content, Intent intent) {
+ String action = intent.getAction();
+ if (WifiManager.WIFI_STATE_CHANGED_ACTION.equals(action)) {
+ int state = intent.getIntExtra(
+ WifiManager.EXTRA_WIFI_STATE, WifiManager.WIFI_STATE_DISABLED);
+ if (state == WifiManager.WIFI_STATE_DISABLED) {
+ mActivity.finish();
+ }
+ } else if (WifiManager.NETWORK_STATE_CHANGED_ACTION.equals(action)) {
+ NetworkInfo networkInfo = (NetworkInfo) intent.getParcelableExtra(
+ WifiManager.EXTRA_NETWORK_INFO);
+ if (networkInfo.getType() == ConnectivityManager.TYPE_WIFI) {
+ if (!networkInfo.isConnected()) {
+ mActivity.finish();
+ return;
+ }
+ WifiInfo wifiInfo = mWifiManager.getConnectionInfo();
+ if (wifiInfo == null || wifiInfo.getNetworkId() == -1) {
+ mActivity.finish();
+ return;
+ }
+ }
+ }
+ }
+ }
+
+ @Override
+ public void onStart() {
+ super.onStart();
+
+ IntentFilter filter = new IntentFilter(WifiManager.WIFI_STATE_CHANGED_ACTION);
+ filter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
+ registerReceiver(mWifiChangeReceiver, filter);
+ }
+
+ @Override
+ protected void onStop() {
+ if (mWifiChangeReceiver != null) {
+ unregisterReceiver(mWifiChangeReceiver);
+ }
+ super.onStop();
+ }
+
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ finish();
+ }
+}
diff --git a/packages/Tethering/Android.bp b/packages/Tethering/Android.bp
index 4efe934..0c37235 100644
--- a/packages/Tethering/Android.bp
+++ b/packages/Tethering/Android.bp
@@ -16,7 +16,9 @@
java_defaults {
name: "TetheringAndroidLibraryDefaults",
- sdk_version: "system_current",
+ // TODO (b/146757305): change to module API once available
+ // TODO (b/148190005): change to module-libs-api-stubs-current once it is ready.
+ sdk_version: "core_platform",
srcs: [
"src/**/*.java",
":framework-tethering-shared-srcs",
@@ -33,8 +35,15 @@
"net-utils-framework-common",
],
libs: [
+ // Order matters: framework-tethering needs to be before the system stubs, otherwise
+ // hidden fields in the framework-tethering classes (which are also used to generate stubs)
+ // will not be found.
"framework-tethering",
+ "android_system_stubs_current",
+ "framework-res",
"unsupportedappusage",
+ "android_system_stubs_current",
+ "framework-res",
],
plugins: ["java_api_finder"],
manifest: "AndroidManifestBase.xml",
@@ -82,20 +91,23 @@
// Common defaults for compiling the actual APK.
java_defaults {
name: "TetheringAppDefaults",
- sdk_version: "system_current",
+ // TODO (b/146757305): change to module API once available
+ // TODO (b/148190005): change to module-libs-api-stubs-current once it is ready.
+ sdk_version: "core_platform",
privileged: true,
- // Build system doesn't track transitive dependeicies for jni_libs, list all the dependencies
- // explicitly.
jni_libs: [
- "liblog",
- "libnativehelper_compat_libc++",
"libtetherutilsjni",
],
resource_dirs: [
"res",
],
libs: [
+ // Order matters: framework-tethering needs to be before the system stubs, otherwise
+ // hidden fields in the framework-tethering classes (which are also used to generate stubs)
+ // will not be found.
"framework-tethering",
+ "android_system_stubs_current",
+ "framework-res",
],
jarjar_rules: "jarjar-rules.txt",
optimize: {
diff --git a/packages/Tethering/TEST_MAPPING b/packages/Tethering/TEST_MAPPING
new file mode 100644
index 0000000..73254cd
--- /dev/null
+++ b/packages/Tethering/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+ "postsubmit": [
+ {
+ "name": "TetheringTests"
+ }
+ ]
+}
diff --git a/packages/Tethering/apex/Android.bp b/packages/Tethering/apex/Android.bp
index 94ef11c..96a4d20 100644
--- a/packages/Tethering/apex/Android.bp
+++ b/packages/Tethering/apex/Android.bp
@@ -16,6 +16,7 @@
apex {
name: "com.android.tethering",
+ updatable: true,
java_libs: ["framework-tethering"],
apps: ["Tethering"],
manifest: "manifest.json",
diff --git a/packages/Tethering/common/TetheringLib/Android.bp b/packages/Tethering/common/TetheringLib/Android.bp
index e0adb34d..5b73dd5 100644
--- a/packages/Tethering/common/TetheringLib/Android.bp
+++ b/packages/Tethering/common/TetheringLib/Android.bp
@@ -41,12 +41,11 @@
java_library {
name: "framework-tethering",
- sdk_version: "system_current",
+ sdk_version: "module_current",
srcs: [
"src/android/net/TetheredClient.java",
"src/android/net/TetheringManager.java",
"src/android/net/TetheringConstants.java",
- ":framework-tethering-annotations",
],
static_libs: [
"tethering-aidl-interfaces-java",
@@ -55,20 +54,36 @@
installable: true,
libs: [
- "android_system_stubs_current",
+ "framework-annotations-lib",
],
hostdex: true, // for hiddenapi check
- visibility: [
- "//frameworks/base/packages/Tethering:__subpackages__",
- //TODO(b/147200698) remove below lines when the platform is built with stubs
- "//frameworks/base",
- "//frameworks/base/services",
- "//frameworks/base/services/core",
- ],
+ visibility: ["//frameworks/base/packages/Tethering:__subpackages__"],
apex_available: ["com.android.tethering"],
}
+droidstubs {
+ name: "framework-tethering-stubs-sources",
+ defaults: ["framework-module-stubs-defaults-module_libs_api"],
+ srcs: [
+ "src/android/net/TetheredClient.java",
+ "src/android/net/TetheringManager.java",
+ "src/android/net/TetheringConstants.java",
+ ],
+ libs: [
+ "tethering-aidl-interfaces-java",
+ "framework-all",
+ ],
+ sdk_version: "core_platform",
+}
+
+java_library {
+ name: "framework-tethering-stubs",
+ srcs: [":framework-tethering-stubs-sources"],
+ libs: ["framework-all"],
+ sdk_version: "core_platform",
+}
+
filegroup {
name: "framework-tethering-srcs",
srcs: [
diff --git a/packages/Tethering/common/TetheringLib/jarjar-rules.txt b/packages/Tethering/common/TetheringLib/jarjar-rules.txt
index 1403bba..e459fad 100644
--- a/packages/Tethering/common/TetheringLib/jarjar-rules.txt
+++ b/packages/Tethering/common/TetheringLib/jarjar-rules.txt
@@ -1,2 +1 @@
-rule android.annotation.** com.android.networkstack.tethering.annotation.@1
-rule com.android.internal.annotations.** com.android.networkstack.tethering.annotation.@1
\ No newline at end of file
+# jarjar rules for the bootclasspath tethering framework library here
\ No newline at end of file
diff --git a/packages/Tethering/common/TetheringLib/src/android/net/ITetheringConnector.aidl b/packages/Tethering/common/TetheringLib/src/android/net/ITetheringConnector.aidl
index 5febe73..8be7964 100644
--- a/packages/Tethering/common/TetheringLib/src/android/net/ITetheringConnector.aidl
+++ b/packages/Tethering/common/TetheringLib/src/android/net/ITetheringConnector.aidl
@@ -1,16 +1,16 @@
-/**
- * Copyright (c) 2019, The Android Open Source Project
+/*
+ * Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
- * http://www.apache.org/licenses/LICENSE-2.0
+ * 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 perNmissions and
+ * See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.net;
diff --git a/packages/Tethering/common/TetheringLib/src/android/net/ITetheringEventCallback.aidl b/packages/Tethering/common/TetheringLib/src/android/net/ITetheringEventCallback.aidl
index 28a810d..a554193 100644
--- a/packages/Tethering/common/TetheringLib/src/android/net/ITetheringEventCallback.aidl
+++ b/packages/Tethering/common/TetheringLib/src/android/net/ITetheringEventCallback.aidl
@@ -17,6 +17,7 @@
package android.net;
import android.net.Network;
+import android.net.TetheredClient;
import android.net.TetheringConfigurationParcel;
import android.net.TetheringCallbackStartedParcel;
import android.net.TetherStatesParcel;
@@ -33,4 +34,5 @@
void onUpstreamChanged(in Network network);
void onConfigurationChanged(in TetheringConfigurationParcel config);
void onTetherStatesChanged(in TetherStatesParcel states);
+ void onTetherClientsChanged(in List<TetheredClient> clients);
}
diff --git a/packages/Tethering/common/TetheringLib/src/android/net/TetheredClient.java b/packages/Tethering/common/TetheringLib/src/android/net/TetheredClient.java
index f5c9664..8b8b9e5 100644
--- a/packages/Tethering/common/TetheringLib/src/android/net/TetheredClient.java
+++ b/packages/Tethering/common/TetheringLib/src/android/net/TetheredClient.java
@@ -16,6 +16,8 @@
package android.net;
+import static android.annotation.SystemApi.Client.MODULE_LIBRARIES;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
@@ -34,6 +36,7 @@
* @hide
*/
@SystemApi
+@SystemApi(client = MODULE_LIBRARIES)
@TestApi
public final class TetheredClient implements Parcelable {
@NonNull
@@ -188,6 +191,15 @@
return new AddressInfo[size];
}
};
+
+ @NonNull
+ @Override
+ public String toString() {
+ return "AddressInfo {"
+ + mAddress
+ + (mHostname != null ? ", hostname " + mHostname : "")
+ + "}";
+ }
}
@Override
@@ -209,4 +221,13 @@
return new TetheredClient[size];
}
};
+
+ @NonNull
+ @Override
+ public String toString() {
+ return "TetheredClient {hwAddr " + mMacAddress
+ + ", addresses " + mAddresses
+ + ", tetheringType " + mTetheringType
+ + "}";
+ }
}
diff --git a/packages/Tethering/common/TetheringLib/src/android/net/TetheringCallbackStartedParcel.aidl b/packages/Tethering/common/TetheringLib/src/android/net/TetheringCallbackStartedParcel.aidl
index 14ee2d3e..c064aa4 100644
--- a/packages/Tethering/common/TetheringLib/src/android/net/TetheringCallbackStartedParcel.aidl
+++ b/packages/Tethering/common/TetheringLib/src/android/net/TetheringCallbackStartedParcel.aidl
@@ -17,6 +17,7 @@
package android.net;
import android.net.Network;
+import android.net.TetheredClient;
import android.net.TetheringConfigurationParcel;
import android.net.TetherStatesParcel;
@@ -29,4 +30,5 @@
Network upstreamNetwork;
TetheringConfigurationParcel config;
TetherStatesParcel states;
+ List<TetheredClient> tetheredClients;
}
\ No newline at end of file
diff --git a/packages/Tethering/common/TetheringLib/src/android/net/TetheringConstants.java b/packages/Tethering/common/TetheringLib/src/android/net/TetheringConstants.java
index 00cf98e..a18f5da 100644
--- a/packages/Tethering/common/TetheringLib/src/android/net/TetheringConstants.java
+++ b/packages/Tethering/common/TetheringLib/src/android/net/TetheringConstants.java
@@ -16,6 +16,9 @@
package android.net;
+import static android.annotation.SystemApi.Client.MODULE_LIBRARIES;
+
+import android.annotation.SystemApi;
import android.os.ResultReceiver;
/**
@@ -28,39 +31,33 @@
* symbols from framework-tethering even when they are in a non-hidden class.
* @hide
*/
+@SystemApi(client = MODULE_LIBRARIES)
public class TetheringConstants {
+ /** An explicit private class to avoid exposing constructor.*/
+ private TetheringConstants() { }
+
/**
* Extra used for communicating with the TetherService. Includes the type of tethering to
* enable if any.
- *
- * {@hide}
*/
public static final String EXTRA_ADD_TETHER_TYPE = "extraAddTetherType";
/**
* Extra used for communicating with the TetherService. Includes the type of tethering for
* which to cancel provisioning.
- *
- * {@hide}
*/
public static final String EXTRA_REM_TETHER_TYPE = "extraRemTetherType";
/**
* Extra used for communicating with the TetherService. True to schedule a recheck of tether
* provisioning.
- *
- * {@hide}
*/
public static final String EXTRA_SET_ALARM = "extraSetAlarm";
/**
* Tells the TetherService to run a provision check now.
- *
- * {@hide}
*/
public static final String EXTRA_RUN_PROVISION = "extraRunProvision";
/**
* Extra used for communicating with the TetherService. Contains the {@link ResultReceiver}
* which will receive provisioning results. Can be left empty.
- *
- * {@hide}
*/
public static final String EXTRA_PROVISION_CALLBACK = "extraProvisionCallback";
}
diff --git a/packages/Tethering/common/TetheringLib/src/android/net/TetheringManager.java b/packages/Tethering/common/TetheringLib/src/android/net/TetheringManager.java
index 53a358f..bfa962a 100644
--- a/packages/Tethering/common/TetheringLib/src/android/net/TetheringManager.java
+++ b/packages/Tethering/common/TetheringLib/src/android/net/TetheringManager.java
@@ -15,6 +15,8 @@
*/
package android.net;
+import static android.annotation.SystemApi.Client.MODULE_LIBRARIES;
+
import android.Manifest;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -50,6 +52,7 @@
* @hide
*/
@SystemApi
+@SystemApi(client = MODULE_LIBRARIES)
@TestApi
public class TetheringManager {
private static final String TAG = TetheringManager.class.getSimpleName();
@@ -177,6 +180,7 @@
* service is not connected.
* {@hide}
*/
+ @SystemApi(client = MODULE_LIBRARIES)
public TetheringManager(@NonNull final Context context,
@NonNull Supplier<IBinder> connectorSupplier) {
mContext = context;
@@ -371,6 +375,9 @@
mTetherStatesParcel = states;
}
+ @Override
+ public void onTetherClientsChanged(List<TetheredClient> clients) { }
+
public void waitForStarted() {
mWaitForCallback.block(DEFAULT_TIMEOUT_MS);
throwIfPermissionFailure(mError);
@@ -395,6 +402,7 @@
* {@hide}
*/
@Deprecated
+ @SystemApi(client = MODULE_LIBRARIES)
public int tether(@NonNull final String iface) {
final String callerPkg = mContext.getOpPackageName();
Log.i(TAG, "tether caller:" + callerPkg);
@@ -418,6 +426,7 @@
* {@hide}
*/
@Deprecated
+ @SystemApi(client = MODULE_LIBRARIES)
public int untether(@NonNull final String iface) {
final String callerPkg = mContext.getOpPackageName();
Log.i(TAG, "untether caller:" + callerPkg);
@@ -444,6 +453,7 @@
* {@hide}
*/
@Deprecated
+ @SystemApi(client = MODULE_LIBRARIES)
public int setUsbTethering(final boolean enable) {
final String callerPkg = mContext.getOpPackageName();
Log.i(TAG, "setUsbTethering caller:" + callerPkg);
@@ -702,6 +712,7 @@
* {@hide}
*/
// TODO: improve the usage of ResultReceiver, b/145096122
+ @SystemApi(client = MODULE_LIBRARIES)
public void requestLatestTetheringEntitlementResult(final int type,
@NonNull final ResultReceiver receiver, final boolean showEntitlementUi) {
final String callerPkg = mContext.getOpPackageName();
@@ -913,6 +924,7 @@
sendRegexpsChanged(parcel.config);
maybeSendTetherableIfacesChangedCallback(parcel.states);
maybeSendTetheredIfacesChangedCallback(parcel.states);
+ callback.onClientsChanged(parcel.tetheredClients);
});
}
@@ -943,6 +955,11 @@
maybeSendTetheredIfacesChangedCallback(states);
});
}
+
+ @Override
+ public void onTetherClientsChanged(final List<TetheredClient> clients) {
+ executor.execute(() -> callback.onClientsChanged(clients));
+ }
};
getConnector(c -> c.registerTetheringEventCallback(remoteCallback, callerPkg));
mTetheringEventCallbacks.put(callback, remoteCallback);
@@ -982,6 +999,7 @@
* interface
* @hide
*/
+ @SystemApi(client = MODULE_LIBRARIES)
public int getLastTetherError(@NonNull final String iface) {
mCallback.waitForStarted();
if (mTetherStatesParcel == null) return TETHER_ERROR_NO_ERROR;
@@ -1004,6 +1022,7 @@
* what interfaces are considered tetherable usb interfaces.
* @hide
*/
+ @SystemApi(client = MODULE_LIBRARIES)
public @NonNull String[] getTetherableUsbRegexs() {
mCallback.waitForStarted();
return mTetheringConfiguration.tetherableUsbRegexs;
@@ -1018,6 +1037,7 @@
* what interfaces are considered tetherable wifi interfaces.
* @hide
*/
+ @SystemApi(client = MODULE_LIBRARIES)
public @NonNull String[] getTetherableWifiRegexs() {
mCallback.waitForStarted();
return mTetheringConfiguration.tetherableWifiRegexs;
@@ -1032,6 +1052,7 @@
* what interfaces are considered tetherable bluetooth interfaces.
* @hide
*/
+ @SystemApi(client = MODULE_LIBRARIES)
public @NonNull String[] getTetherableBluetoothRegexs() {
mCallback.waitForStarted();
return mTetheringConfiguration.tetherableBluetoothRegexs;
@@ -1044,6 +1065,7 @@
* @return an array of 0 or more Strings of tetherable interface names.
* @hide
*/
+ @SystemApi(client = MODULE_LIBRARIES)
public @NonNull String[] getTetherableIfaces() {
mCallback.waitForStarted();
if (mTetherStatesParcel == null) return new String[0];
@@ -1057,6 +1079,7 @@
* @return an array of 0 or more String of currently tethered interface names.
* @hide
*/
+ @SystemApi(client = MODULE_LIBRARIES)
public @NonNull String[] getTetheredIfaces() {
mCallback.waitForStarted();
if (mTetherStatesParcel == null) return new String[0];
@@ -1076,6 +1099,7 @@
* which failed to tether.
* @hide
*/
+ @SystemApi(client = MODULE_LIBRARIES)
public @NonNull String[] getTetheringErroredIfaces() {
mCallback.waitForStarted();
if (mTetherStatesParcel == null) return new String[0];
@@ -1103,6 +1127,7 @@
* @return a boolean - {@code true} indicating Tethering is supported.
* @hide
*/
+ @SystemApi(client = MODULE_LIBRARIES)
public boolean isTetheringSupported() {
final String callerPkg = mContext.getOpPackageName();
diff --git a/packages/Tethering/src/android/net/ip/IpServer.java b/packages/Tethering/src/android/net/ip/IpServer.java
index 2653b6d..38f8609 100644
--- a/packages/Tethering/src/android/net/ip/IpServer.java
+++ b/packages/Tethering/src/android/net/ip/IpServer.java
@@ -19,6 +19,7 @@
import static android.net.InetAddresses.parseNumericAddress;
import static android.net.RouteInfo.RTN_UNICAST;
import static android.net.dhcp.IDhcpServer.STATUS_SUCCESS;
+import static android.net.shared.Inet4AddressUtils.intToInet4AddressHTH;
import static android.net.util.NetworkConstants.FF;
import static android.net.util.NetworkConstants.RFC7421_PREFIX_LENGTH;
import static android.net.util.NetworkConstants.asByte;
@@ -29,18 +30,24 @@
import android.net.IpPrefix;
import android.net.LinkAddress;
import android.net.LinkProperties;
+import android.net.MacAddress;
import android.net.RouteInfo;
+import android.net.TetheredClient;
import android.net.TetheringManager;
+import android.net.dhcp.DhcpLeaseParcelable;
import android.net.dhcp.DhcpServerCallbacks;
import android.net.dhcp.DhcpServingParamsParcel;
import android.net.dhcp.DhcpServingParamsParcelExt;
+import android.net.dhcp.IDhcpLeaseCallbacks;
import android.net.dhcp.IDhcpServer;
+import android.net.ip.IpNeighborMonitor.NeighborEvent;
import android.net.ip.RouterAdvertisementDaemon.RaParams;
import android.net.shared.NetdUtils;
import android.net.shared.RouteUtils;
import android.net.util.InterfaceParams;
import android.net.util.InterfaceSet;
import android.net.util.SharedLog;
+import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.os.RemoteException;
@@ -48,16 +55,24 @@
import android.util.Log;
import android.util.SparseArray;
+import androidx.annotation.NonNull;
+
import com.android.internal.util.MessageUtils;
import com.android.internal.util.State;
import com.android.internal.util.StateMachine;
+import java.io.IOException;
import java.net.Inet4Address;
import java.net.Inet6Address;
import java.net.InetAddress;
+import java.net.NetworkInterface;
import java.net.UnknownHostException;
import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
import java.util.HashSet;
+import java.util.LinkedHashMap;
+import java.util.List;
import java.util.Objects;
import java.util.Random;
import java.util.Set;
@@ -130,10 +145,21 @@
* @param newLp the new LinkProperties to report
*/
public void updateLinkProperties(IpServer who, LinkProperties newLp) { }
+
+ /**
+ * Notify that the DHCP leases changed in one of the IpServers.
+ */
+ public void dhcpLeasesChanged() { }
}
/** Capture IpServer dependencies, for injection. */
public abstract static class Dependencies {
+ /** Create an IpNeighborMonitor to be used by this IpServer */
+ public IpNeighborMonitor getIpNeighborMonitor(Handler handler, SharedLog log,
+ IpNeighborMonitor.NeighborEventConsumer consumer) {
+ return new IpNeighborMonitor(handler, log, consumer);
+ }
+
/** Create a RouterAdvertisementDaemon instance to be used by IpServer.*/
public RouterAdvertisementDaemon getRouterAdvertisementDaemon(InterfaceParams ifParams) {
return new RouterAdvertisementDaemon(ifParams);
@@ -144,6 +170,15 @@
return InterfaceParams.getByName(ifName);
}
+ /** Get |ifName|'s interface index. */
+ public int getIfindex(String ifName) {
+ try {
+ return NetworkInterface.getByName(ifName).getIndex();
+ } catch (IOException | NullPointerException e) {
+ Log.e(TAG, "Can't determine interface index for interface " + ifName);
+ return 0;
+ }
+ }
/** Create a DhcpServer instance to be used by IpServer. */
public abstract void makeDhcpServer(String ifName, DhcpServingParamsParcel params,
DhcpServerCallbacks cb);
@@ -169,6 +204,8 @@
public static final int CMD_TETHER_CONNECTION_CHANGED = BASE_IPSERVER + 9;
// new IPv6 tethering parameters need to be processed
public static final int CMD_IPV6_TETHER_UPDATE = BASE_IPSERVER + 10;
+ // new neighbor cache entry on our interface
+ public static final int CMD_NEIGHBOR_EVENT = BASE_IPSERVER + 11;
private final State mInitialState;
private final State mLocalHotspotState;
@@ -205,6 +242,42 @@
private IDhcpServer mDhcpServer;
private RaParams mLastRaParams;
private LinkAddress mIpv4Address;
+ @NonNull
+ private List<TetheredClient> mDhcpLeases = Collections.emptyList();
+
+ private int mLastIPv6UpstreamIfindex = 0;
+
+ private class MyNeighborEventConsumer implements IpNeighborMonitor.NeighborEventConsumer {
+ public void accept(NeighborEvent e) {
+ sendMessage(CMD_NEIGHBOR_EVENT, e);
+ }
+ }
+
+ static class Ipv6ForwardingRule {
+ public final int upstreamIfindex;
+ public final int downstreamIfindex;
+ public final Inet6Address address;
+ public final MacAddress srcMac;
+ public final MacAddress dstMac;
+
+ Ipv6ForwardingRule(int upstreamIfindex, int downstreamIfIndex, Inet6Address address,
+ MacAddress srcMac, MacAddress dstMac) {
+ this.upstreamIfindex = upstreamIfindex;
+ this.downstreamIfindex = downstreamIfIndex;
+ this.address = address;
+ this.srcMac = srcMac;
+ this.dstMac = dstMac;
+ }
+
+ public Ipv6ForwardingRule onNewUpstream(int newUpstreamIfindex) {
+ return new Ipv6ForwardingRule(newUpstreamIfindex, downstreamIfindex, address, srcMac,
+ dstMac);
+ }
+ }
+ private final LinkedHashMap<Inet6Address, Ipv6ForwardingRule> mIpv6ForwardingRules =
+ new LinkedHashMap<>();
+
+ private final IpNeighborMonitor mIpNeighborMonitor;
public IpServer(
String ifaceName, Looper looper, int interfaceType, SharedLog log,
@@ -223,6 +296,12 @@
mLastError = TetheringManager.TETHER_ERROR_NO_ERROR;
mServingMode = STATE_AVAILABLE;
+ mIpNeighborMonitor = mDeps.getIpNeighborMonitor(getHandler(), mLog,
+ new MyNeighborEventConsumer());
+ if (!mIpNeighborMonitor.start()) {
+ mLog.e("Failed to create IpNeighborMonitor on " + mIfaceName);
+ }
+
mInitialState = new InitialState();
mLocalHotspotState = new LocalHotspotState();
mTetheredState = new TetheredState();
@@ -262,6 +341,14 @@
return new LinkProperties(mLinkProperties);
}
+ /**
+ * Get the latest list of DHCP leases that was reported. Must be called on the IpServer looper
+ * thread.
+ */
+ public List<TetheredClient> getAllLeases() {
+ return Collections.unmodifiableList(mDhcpLeases);
+ }
+
/** Stop this IpServer. After this is called this IpServer should not be used any more. */
public void stop() {
sendMessage(CMD_INTERFACE_DOWN);
@@ -334,7 +421,7 @@
mDhcpServer = server;
try {
- mDhcpServer.start(new OnHandlerStatusCallback() {
+ mDhcpServer.startWithCallbacks(new OnHandlerStatusCallback() {
@Override
public void callback(int startStatusCode) {
if (startStatusCode != STATUS_SUCCESS) {
@@ -342,7 +429,7 @@
handleError();
}
}
- });
+ }, new DhcpLeaseCallback());
} catch (RemoteException e) {
throw new IllegalStateException(e);
}
@@ -355,6 +442,48 @@
}
}
+ private class DhcpLeaseCallback extends IDhcpLeaseCallbacks.Stub {
+ @Override
+ public void onLeasesChanged(List<DhcpLeaseParcelable> leaseParcelables) {
+ final ArrayList<TetheredClient> leases = new ArrayList<>();
+ for (DhcpLeaseParcelable lease : leaseParcelables) {
+ final LinkAddress address = new LinkAddress(
+ intToInet4AddressHTH(lease.netAddr), lease.prefixLength);
+
+ final MacAddress macAddress;
+ try {
+ macAddress = MacAddress.fromBytes(lease.hwAddr);
+ } catch (IllegalArgumentException e) {
+ Log.wtf(TAG, "Invalid address received from DhcpServer: "
+ + Arrays.toString(lease.hwAddr));
+ return;
+ }
+
+ final TetheredClient.AddressInfo addressInfo = new TetheredClient.AddressInfo(
+ address, lease.hostname, lease.expTime);
+ leases.add(new TetheredClient(
+ macAddress,
+ Collections.singletonList(addressInfo),
+ mInterfaceType));
+ }
+
+ getHandler().post(() -> {
+ mDhcpLeases = leases;
+ mCallback.dhcpLeasesChanged();
+ });
+ }
+
+ @Override
+ public int getInterfaceVersion() {
+ return this.VERSION;
+ }
+
+ @Override
+ public String getInterfaceHash() throws RemoteException {
+ return this.HASH;
+ }
+ }
+
private boolean startDhcp(Inet4Address addr, int prefixLen) {
if (mUsingLegacyDhcp) {
return true;
@@ -388,6 +517,8 @@
mLastError = TetheringManager.TETHER_ERROR_DHCPSERVER_ERROR;
// Not much more we can do here
}
+ mDhcpLeases.clear();
+ getHandler().post(mCallback::dhcpLeasesChanged);
}
});
mDhcpServer = null;
@@ -538,13 +669,21 @@
}
RaParams params = null;
+ int upstreamIfindex = 0;
if (v6only != null) {
+ final String upstreamIface = v6only.getInterfaceName();
+
params = new RaParams();
- params.mtu = v6only.getMtu();
+ // We advertise an mtu lower by 16, which is the closest multiple of 8 >= 14,
+ // the ethernet header size. This makes kernel ebpf tethering offload happy.
+ // This hack should be reverted once we have the kernel fixed up.
+ // Note: this will automatically clamp to at least 1280 (ipv6 minimum mtu)
+ // see RouterAdvertisementDaemon.java putMtu()
+ params.mtu = v6only.getMtu() - 16;
params.hasDefaultRoute = v6only.hasIpv6DefaultRoute();
- if (params.hasDefaultRoute) params.hopLimit = getHopLimit(v6only.getInterfaceName());
+ if (params.hasDefaultRoute) params.hopLimit = getHopLimit(upstreamIface);
for (LinkAddress linkAddr : v6only.getLinkAddresses()) {
if (linkAddr.getPrefixLength() != RFC7421_PREFIX_LENGTH) continue;
@@ -558,12 +697,18 @@
params.dnses.add(dnsServer);
}
}
+
+ upstreamIfindex = mDeps.getIfindex(upstreamIface);
}
+
// If v6only is null, we pass in null to setRaParams(), which handles
// deprecation of any existing RA data.
setRaParams(params);
mLastIPv6LinkProperties = v6only;
+
+ updateIpv6ForwardingRules(mLastIPv6UpstreamIfindex, upstreamIfindex, null);
+ mLastIPv6UpstreamIfindex = upstreamIfindex;
}
private void configureLocalIPv6Routes(
@@ -658,6 +803,87 @@
}
}
+ private void addIpv6ForwardingRule(Ipv6ForwardingRule rule) {
+ try {
+ mNetd.tetherRuleAddDownstreamIpv6(mInterfaceParams.index, rule.upstreamIfindex,
+ rule.address.getAddress(), mInterfaceParams.macAddr.toByteArray(),
+ rule.dstMac.toByteArray());
+ mIpv6ForwardingRules.put(rule.address, rule);
+ } catch (RemoteException | ServiceSpecificException e) {
+ mLog.e("Could not add IPv6 downstream rule: ", e);
+ }
+ }
+
+ private void removeIpv6ForwardingRule(Ipv6ForwardingRule rule, boolean removeFromMap) {
+ try {
+ mNetd.tetherRuleRemoveDownstreamIpv6(rule.upstreamIfindex, rule.address.getAddress());
+ if (removeFromMap) {
+ mIpv6ForwardingRules.remove(rule.address);
+ }
+ } catch (RemoteException | ServiceSpecificException e) {
+ mLog.e("Could not remove IPv6 downstream rule: ", e);
+ }
+ }
+
+ private void clearIpv6ForwardingRules() {
+ for (Ipv6ForwardingRule rule : mIpv6ForwardingRules.values()) {
+ removeIpv6ForwardingRule(rule, false /*removeFromMap*/);
+ }
+ mIpv6ForwardingRules.clear();
+ }
+
+ // Convenience method to replace a rule with the same rule on a new upstream interface.
+ // Allows replacing the rules in one iteration pass without ConcurrentModificationExceptions.
+ // Relies on the fact that rules are in a map indexed by IP address.
+ private void updateIpv6ForwardingRule(Ipv6ForwardingRule rule, int newIfindex) {
+ addIpv6ForwardingRule(rule.onNewUpstream(newIfindex));
+ removeIpv6ForwardingRule(rule, false /*removeFromMap*/);
+ }
+
+ // Handles all updates to IPv6 forwarding rules. These can currently change only if the upstream
+ // changes or if a neighbor event is received.
+ private void updateIpv6ForwardingRules(int prevUpstreamIfindex, int upstreamIfindex,
+ NeighborEvent e) {
+ // If we no longer have an upstream, clear forwarding rules and do nothing else.
+ if (upstreamIfindex == 0) {
+ clearIpv6ForwardingRules();
+ return;
+ }
+
+ // If the upstream interface has changed, remove all rules and re-add them with the new
+ // upstream interface.
+ if (prevUpstreamIfindex != upstreamIfindex) {
+ for (Ipv6ForwardingRule rule : mIpv6ForwardingRules.values()) {
+ updateIpv6ForwardingRule(rule, upstreamIfindex);
+ }
+ }
+
+ // If we're here to process a NeighborEvent, do so now.
+ // mInterfaceParams must be non-null or the event would not have arrived.
+ if (e == null) return;
+ if (!(e.ip instanceof Inet6Address) || e.ip.isMulticastAddress()
+ || e.ip.isLoopbackAddress() || e.ip.isLinkLocalAddress()) {
+ return;
+ }
+
+ Ipv6ForwardingRule rule = new Ipv6ForwardingRule(upstreamIfindex,
+ mInterfaceParams.index, (Inet6Address) e.ip, mInterfaceParams.macAddr,
+ e.macAddr);
+ if (e.isValid()) {
+ addIpv6ForwardingRule(rule);
+ } else {
+ removeIpv6ForwardingRule(rule, true /*removeFromMap*/);
+ }
+ }
+
+ private void handleNeighborEvent(NeighborEvent e) {
+ if (mInterfaceParams != null
+ && mInterfaceParams.index == e.ifindex
+ && mInterfaceParams.hasMacAddress) {
+ updateIpv6ForwardingRules(mLastIPv6UpstreamIfindex, mLastIPv6UpstreamIfindex, e);
+ }
+ }
+
private byte getHopLimit(String upstreamIface) {
try {
int upstreamHopLimit = Integer.parseUnsignedInt(
@@ -883,6 +1109,7 @@
for (String ifname : mUpstreamIfaceSet.ifnames) cleanupUpstreamInterface(ifname);
mUpstreamIfaceSet = null;
+ clearIpv6ForwardingRules();
}
private void cleanupUpstreamInterface(String upstreamIface) {
@@ -945,6 +1172,9 @@
}
}
break;
+ case CMD_NEIGHBOR_EVENT:
+ handleNeighborEvent((NeighborEvent) message.obj);
+ break;
default:
return false;
}
diff --git a/packages/Tethering/src/com/android/server/connectivity/tethering/ConnectedClientsTracker.java b/packages/Tethering/src/com/android/server/connectivity/tethering/ConnectedClientsTracker.java
new file mode 100644
index 0000000..cdd1a5d
--- /dev/null
+++ b/packages/Tethering/src/com/android/server/connectivity/tethering/ConnectedClientsTracker.java
@@ -0,0 +1,183 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.connectivity.tethering;
+
+import static android.net.TetheringManager.TETHERING_WIFI;
+
+import android.net.MacAddress;
+import android.net.TetheredClient;
+import android.net.TetheredClient.AddressInfo;
+import android.net.ip.IpServer;
+import android.net.wifi.WifiClient;
+import android.os.SystemClock;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.VisibleForTesting;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Tracker for clients connected to downstreams.
+ *
+ * <p>This class is not thread safe, it is intended to be used only from the tethering handler
+ * thread.
+ */
+public class ConnectedClientsTracker {
+ private final Clock mClock;
+
+ @NonNull
+ private List<WifiClient> mLastWifiClients = Collections.emptyList();
+ @NonNull
+ private List<TetheredClient> mLastTetheredClients = Collections.emptyList();
+
+ @VisibleForTesting
+ static class Clock {
+ public long elapsedRealtime() {
+ return SystemClock.elapsedRealtime();
+ }
+ }
+
+ public ConnectedClientsTracker() {
+ this(new Clock());
+ }
+
+ @VisibleForTesting
+ ConnectedClientsTracker(Clock clock) {
+ mClock = clock;
+ }
+
+ /**
+ * Update the tracker with new connected clients.
+ *
+ * <p>The new list can be obtained through {@link #getLastTetheredClients()}.
+ * @param ipServers The IpServers used to assign addresses to clients.
+ * @param wifiClients The list of L2-connected WiFi clients. Null for no change since last
+ * update.
+ * @return True if the list of clients changed since the last calculation.
+ */
+ public boolean updateConnectedClients(
+ Iterable<IpServer> ipServers, @Nullable List<WifiClient> wifiClients) {
+ final long now = mClock.elapsedRealtime();
+
+ if (wifiClients != null) {
+ mLastWifiClients = wifiClients;
+ }
+ final Set<MacAddress> wifiClientMacs = getClientMacs(mLastWifiClients);
+
+ // Build the list of non-expired leases from all IpServers, grouped by mac address
+ final Map<MacAddress, TetheredClient> clientsMap = new HashMap<>();
+ for (IpServer server : ipServers) {
+ for (TetheredClient client : server.getAllLeases()) {
+ if (client.getTetheringType() == TETHERING_WIFI
+ && !wifiClientMacs.contains(client.getMacAddress())) {
+ // Skip leases of WiFi clients that are not (or no longer) L2-connected
+ continue;
+ }
+ final TetheredClient prunedClient = pruneExpired(client, now);
+ if (prunedClient == null) continue; // All addresses expired
+
+ addLease(clientsMap, prunedClient);
+ }
+ }
+
+ // TODO: add IPv6 addresses from netlink
+
+ // Add connected WiFi clients that do not have any known address
+ for (MacAddress client : wifiClientMacs) {
+ if (clientsMap.containsKey(client)) continue;
+ clientsMap.put(client, new TetheredClient(
+ client, Collections.emptyList() /* addresses */, TETHERING_WIFI));
+ }
+
+ final HashSet<TetheredClient> clients = new HashSet<>(clientsMap.values());
+ final boolean clientsChanged = clients.size() != mLastTetheredClients.size()
+ || !clients.containsAll(mLastTetheredClients);
+ mLastTetheredClients = Collections.unmodifiableList(new ArrayList<>(clients));
+ return clientsChanged;
+ }
+
+ private static void addLease(Map<MacAddress, TetheredClient> clientsMap, TetheredClient lease) {
+ final TetheredClient aggregateClient = clientsMap.getOrDefault(
+ lease.getMacAddress(), lease);
+ if (aggregateClient == lease) {
+ // This is the first lease with this mac address
+ clientsMap.put(lease.getMacAddress(), lease);
+ return;
+ }
+
+ // Only add the address info; this assumes that the tethering type is the same when the mac
+ // address is the same. If a client is connected through different tethering types with the
+ // same mac address, connected clients callbacks will report all of its addresses under only
+ // one of these tethering types. This keeps the API simple considering that such a scenario
+ // would really be a rare edge case.
+ clientsMap.put(lease.getMacAddress(), aggregateClient.addAddresses(lease));
+ }
+
+ /**
+ * Get the last list of tethered clients, as calculated in {@link #updateConnectedClients}.
+ *
+ * <p>The returned list is immutable.
+ */
+ @NonNull
+ public List<TetheredClient> getLastTetheredClients() {
+ return mLastTetheredClients;
+ }
+
+ private static boolean hasExpiredAddress(List<AddressInfo> addresses, long now) {
+ for (AddressInfo info : addresses) {
+ if (info.getExpirationTime() <= now) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @Nullable
+ private static TetheredClient pruneExpired(TetheredClient client, long now) {
+ final List<AddressInfo> addresses = client.getAddresses();
+ if (addresses.size() == 0) return null;
+ if (!hasExpiredAddress(addresses, now)) return client;
+
+ final ArrayList<AddressInfo> newAddrs = new ArrayList<>(addresses.size() - 1);
+ for (AddressInfo info : addresses) {
+ if (info.getExpirationTime() > now) {
+ newAddrs.add(info);
+ }
+ }
+
+ if (newAddrs.size() == 0) {
+ return null;
+ }
+ return new TetheredClient(client.getMacAddress(), newAddrs, client.getTetheringType());
+ }
+
+ @NonNull
+ private static Set<MacAddress> getClientMacs(@NonNull List<WifiClient> clients) {
+ final Set<MacAddress> macs = new HashSet<>(clients.size());
+ for (WifiClient c : clients) {
+ macs.add(c.getMacAddress());
+ }
+ return macs;
+ }
+}
diff --git a/packages/Tethering/src/com/android/server/connectivity/tethering/OffloadController.java b/packages/Tethering/src/com/android/server/connectivity/tethering/OffloadController.java
index cc36f4a..a402ffa 100644
--- a/packages/Tethering/src/com/android/server/connectivity/tethering/OffloadController.java
+++ b/packages/Tethering/src/com/android/server/connectivity/tethering/OffloadController.java
@@ -288,10 +288,18 @@
@Override
public void setLimit(String iface, long quotaBytes) {
- mLog.i("setLimit: " + iface + "," + quotaBytes);
// Listen for all iface is necessary since upstream might be changed after limit
// is set.
mHandler.post(() -> {
+ final Long curIfaceQuota = mInterfaceQuotas.get(iface);
+
+ // If the quota is set to unlimited, the value set to HAL is Long.MAX_VALUE,
+ // which is ~8.4 x 10^6 TiB, no one can actually reach it. Thus, it is not
+ // useful to set it multiple times.
+ // Otherwise, the quota needs to be updated to tell HAL to re-count from now even
+ // if the quota is the same as the existing one.
+ if (null == curIfaceQuota && QUOTA_UNLIMITED == quotaBytes) return;
+
if (quotaBytes == QUOTA_UNLIMITED) {
mInterfaceQuotas.remove(iface);
} else {
@@ -323,7 +331,6 @@
@Override
public void requestStatsUpdate(int token) {
- mLog.i("requestStatsUpdate: " + token);
// Do not attempt to update stats by querying the offload HAL
// synchronously from a different thread than the Handler thread. http://b/64771555.
mHandler.post(() -> {
diff --git a/packages/Tethering/src/com/android/server/connectivity/tethering/Tethering.java b/packages/Tethering/src/com/android/server/connectivity/tethering/Tethering.java
index 1edbbf8..0958e8a 100644
--- a/packages/Tethering/src/com/android/server/connectivity/tethering/Tethering.java
+++ b/packages/Tethering/src/com/android/server/connectivity/tethering/Tethering.java
@@ -16,6 +16,8 @@
package com.android.server.connectivity.tethering;
+import static android.Manifest.permission.NETWORK_SETTINGS;
+import static android.Manifest.permission.NETWORK_STACK;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.hardware.usb.UsbManager.USB_CONFIGURED;
import static android.hardware.usb.UsbManager.USB_CONNECTED;
@@ -24,6 +26,7 @@
import static android.net.ConnectivityManager.ACTION_RESTRICT_BACKGROUND_CHANGED;
import static android.net.ConnectivityManager.CONNECTIVITY_ACTION;
import static android.net.ConnectivityManager.EXTRA_NETWORK_INFO;
+import static android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK;
import static android.net.TetheringManager.ACTION_TETHER_STATE_CHANGED;
import static android.net.TetheringManager.EXTRA_ACTIVE_LOCAL_ONLY;
import static android.net.TetheringManager.EXTRA_ACTIVE_TETHER;
@@ -79,6 +82,7 @@
import android.net.Network;
import android.net.NetworkInfo;
import android.net.TetherStatesParcel;
+import android.net.TetheredClient;
import android.net.TetheringCallbackStartedParcel;
import android.net.TetheringConfigurationParcel;
import android.net.TetheringRequestParcel;
@@ -89,6 +93,7 @@
import android.net.util.PrefixUtils;
import android.net.util.SharedLog;
import android.net.util.VersionedBroadcastListener;
+import android.net.wifi.WifiClient;
import android.net.wifi.WifiManager;
import android.net.wifi.p2p.WifiP2pGroup;
import android.net.wifi.p2p.WifiP2pInfo;
@@ -128,8 +133,10 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
-import java.util.HashSet;
+import java.util.Collections;
import java.util.Iterator;
+import java.util.LinkedHashSet;
+import java.util.List;
import java.util.Set;
import java.util.concurrent.Executor;
import java.util.concurrent.RejectedExecutionException;
@@ -176,6 +183,17 @@
}
}
+ /**
+ * Cookie added when registering {@link android.net.TetheringManager.TetheringEventCallback}.
+ */
+ private static class CallbackCookie {
+ public final boolean hasListClientsPermission;
+
+ private CallbackCookie(boolean hasListClientsPermission) {
+ this.hasListClientsPermission = hasListClientsPermission;
+ }
+ }
+
private final SharedLog mLog = new SharedLog(TAG);
private final RemoteCallbackList<ITetheringEventCallback> mTetheringEventCallbacks =
new RemoteCallbackList<>();
@@ -191,7 +209,8 @@
private final UpstreamNetworkMonitor mUpstreamNetworkMonitor;
// TODO: Figure out how to merge this and other downstream-tracking objects
// into a single coherent structure.
- private final HashSet<IpServer> mForwardedDownstreams;
+ // Use LinkedHashSet for predictable ordering order for ConnectedClientsTracker.
+ private final LinkedHashSet<IpServer> mForwardedDownstreams;
private final VersionedBroadcastListener mCarrierConfigChange;
private final TetheringDependencies mDeps;
private final EntitlementManager mEntitlementMgr;
@@ -200,6 +219,8 @@
private final NetdCallback mNetdCallback;
private final UserRestrictionActionListener mTetheringRestriction;
private final ActiveDataSubIdListener mActiveDataSubIdListener;
+ private final ConnectedClientsTracker mConnectedClientsTracker;
+ private final TetheringThreadExecutor mExecutor;
private int mActiveDataSubId = INVALID_SUBSCRIPTION_ID;
// All the usage of mTetheringEventCallback should run in the same thread.
private ITetheringEventCallback mTetheringEventCallback = null;
@@ -234,6 +255,7 @@
mPublicSync = new Object();
mTetherStates = new ArrayMap<>();
+ mConnectedClientsTracker = new ConnectedClientsTracker();
mTetherMasterSM = new TetherMasterSM("TetherMaster", mLooper, deps);
mTetherMasterSM.start();
@@ -246,7 +268,7 @@
statsManager, mLog);
mUpstreamNetworkMonitor = mDeps.getUpstreamNetworkMonitor(mContext, mTetherMasterSM, mLog,
TetherMasterSM.EVENT_UPSTREAM_CALLBACK);
- mForwardedDownstreams = new HashSet<>();
+ mForwardedDownstreams = new LinkedHashSet<>();
IntentFilter filter = new IntentFilter();
filter.addAction(ACTION_CARRIER_CONFIG_CHANGED);
@@ -275,8 +297,8 @@
final UserManager userManager = (UserManager) mContext.getSystemService(
Context.USER_SERVICE);
mTetheringRestriction = new UserRestrictionActionListener(userManager, this);
- final TetheringThreadExecutor executor = new TetheringThreadExecutor(mHandler);
- mActiveDataSubIdListener = new ActiveDataSubIdListener(executor);
+ mExecutor = new TetheringThreadExecutor(mHandler);
+ mActiveDataSubIdListener = new ActiveDataSubIdListener(mExecutor);
// Load tethering configuration.
updateConfiguration();
@@ -291,6 +313,11 @@
startStateMachineUpdaters(mHandler);
startTrackDefaultNetwork();
+
+ final WifiManager wifiManager = getWifiManager();
+ if (wifiManager != null) {
+ wifiManager.registerSoftApCallback(mExecutor, new TetheringSoftApCallback());
+ }
}
private void startStateMachineUpdaters(Handler handler) {
@@ -385,6 +412,24 @@
}
}
+ private class TetheringSoftApCallback implements WifiManager.SoftApCallback {
+ // TODO: Remove onStateChanged override when this method has default on
+ // WifiManager#SoftApCallback interface.
+ // Wifi listener for state change of the soft AP
+ @Override
+ public void onStateChanged(final int state, final int failureReason) {
+ // Nothing
+ }
+
+ // Called by wifi when the number of soft AP clients changed.
+ @Override
+ public void onConnectedClientsChanged(final List<WifiClient> clients) {
+ if (mConnectedClientsTracker.updateConnectedClients(mForwardedDownstreams, clients)) {
+ reportTetherClientsChanged(mConnectedClientsTracker.getLastTetheredClients());
+ }
+ }
+ }
+
void interfaceStatusChanged(String iface, boolean up) {
// Never called directly: only called from interfaceLinkStateChanged.
// See NetlinkHandler.cpp: notifyInterfaceChanged.
@@ -560,14 +605,17 @@
Context.ETHERNET_SERVICE);
synchronized (mPublicSync) {
if (enable) {
+ if (mEthernetCallback != null) return TETHER_ERROR_NO_ERROR;
+
mEthernetCallback = new EthernetCallback();
- mEthernetIfaceRequest = em.requestTetheredInterface(mEthernetCallback);
+ mEthernetIfaceRequest = em.requestTetheredInterface(mExecutor, mEthernetCallback);
} else {
- if (mConfiguredEthernetIface != null) {
- stopEthernetTetheringLocked();
+ stopEthernetTetheringLocked();
+ if (mEthernetCallback != null) {
mEthernetIfaceRequest.release();
+ mEthernetCallback = null;
+ mEthernetIfaceRequest = null;
}
- mEthernetCallback = null;
}
}
return TETHER_ERROR_NO_ERROR;
@@ -1938,14 +1986,21 @@
/** Register tethering event callback */
void registerTetheringEventCallback(ITetheringEventCallback callback) {
+ final boolean hasListPermission =
+ hasCallingPermission(NETWORK_SETTINGS)
+ || hasCallingPermission(PERMISSION_MAINLINE_NETWORK_STACK)
+ || hasCallingPermission(NETWORK_STACK);
mHandler.post(() -> {
- mTetheringEventCallbacks.register(callback);
+ mTetheringEventCallbacks.register(callback, new CallbackCookie(hasListPermission));
final TetheringCallbackStartedParcel parcel = new TetheringCallbackStartedParcel();
parcel.tetheringSupported = mDeps.isTetheringSupported();
parcel.upstreamNetwork = mTetherUpstream;
parcel.config = mConfig.toStableParcelable();
parcel.states =
mTetherStatesParcel != null ? mTetherStatesParcel : emptyTetherStatesParcel();
+ parcel.tetheredClients = hasListPermission
+ ? mConnectedClientsTracker.getLastTetheredClients()
+ : Collections.emptyList();
try {
callback.onCallbackStarted(parcel);
} catch (RemoteException e) {
@@ -1965,6 +2020,10 @@
return parcel;
}
+ private boolean hasCallingPermission(@NonNull String permission) {
+ return mContext.checkCallingPermission(permission) == PERMISSION_GRANTED;
+ }
+
/** Unregister tethering event callback */
void unregisterTetheringEventCallback(ITetheringEventCallback callback) {
mHandler.post(() -> {
@@ -2018,6 +2077,24 @@
}
}
+ private void reportTetherClientsChanged(List<TetheredClient> clients) {
+ final int length = mTetheringEventCallbacks.beginBroadcast();
+ try {
+ for (int i = 0; i < length; i++) {
+ try {
+ final CallbackCookie cookie =
+ (CallbackCookie) mTetheringEventCallbacks.getBroadcastCookie(i);
+ if (!cookie.hasListClientsPermission) continue;
+ mTetheringEventCallbacks.getBroadcastItem(i).onTetherClientsChanged(clients);
+ } catch (RemoteException e) {
+ // Not really very much to do here.
+ }
+ }
+ } finally {
+ mTetheringEventCallbacks.finishBroadcast();
+ }
+ }
+
void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter writer, @Nullable String[] args) {
// Binder.java closes the resource for us.
@SuppressWarnings("resource")
@@ -2109,6 +2186,14 @@
public void updateLinkProperties(IpServer who, LinkProperties newLp) {
notifyLinkPropertiesChanged(who, newLp);
}
+
+ @Override
+ public void dhcpLeasesChanged() {
+ if (mConnectedClientsTracker.updateConnectedClients(
+ mForwardedDownstreams, null /* wifiClients */)) {
+ reportTetherClientsChanged(mConnectedClientsTracker.getLastTetheredClients());
+ }
+ }
};
}
diff --git a/packages/Tethering/tests/unit/Android.bp b/packages/Tethering/tests/unit/Android.bp
index 13174c5..ddc095f 100644
--- a/packages/Tethering/tests/unit/Android.bp
+++ b/packages/Tethering/tests/unit/Android.bp
@@ -34,7 +34,13 @@
"TetheringApiCurrentLib",
"testables",
],
+ // TODO(b/147200698) change sdk_version to module-current and
+ // remove framework-minus-apex, ext, and framework-res
+ sdk_version: "core_platform",
libs: [
+ "framework-minus-apex",
+ "ext",
+ "framework-res",
"android.test.runner",
"android.test.base",
"android.test.mock",
diff --git a/packages/Tethering/tests/unit/src/android/net/ip/IpServerTest.java b/packages/Tethering/tests/unit/src/android/net/ip/IpServerTest.java
index f29ad78..948266d 100644
--- a/packages/Tethering/tests/unit/src/android/net/ip/IpServerTest.java
+++ b/packages/Tethering/tests/unit/src/android/net/ip/IpServerTest.java
@@ -29,6 +29,11 @@
import static android.net.ip.IpServer.STATE_LOCAL_ONLY;
import static android.net.ip.IpServer.STATE_TETHERED;
import static android.net.ip.IpServer.STATE_UNAVAILABLE;
+import static android.net.netlink.NetlinkConstants.RTM_DELNEIGH;
+import static android.net.netlink.NetlinkConstants.RTM_NEWNEIGH;
+import static android.net.netlink.StructNdMsg.NUD_FAILED;
+import static android.net.netlink.StructNdMsg.NUD_REACHABLE;
+import static android.net.netlink.StructNdMsg.NUD_STALE;
import static android.net.shared.Inet4AddressUtils.intToInet4AddressHTH;
import static org.junit.Assert.assertEquals;
@@ -38,9 +43,11 @@
import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyInt;
import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.never;
@@ -52,6 +59,7 @@
import static org.mockito.Mockito.when;
import android.net.INetd;
+import android.net.InetAddresses;
import android.net.InterfaceConfigurationParcel;
import android.net.IpPrefix;
import android.net.LinkAddress;
@@ -61,6 +69,8 @@
import android.net.dhcp.DhcpServingParamsParcel;
import android.net.dhcp.IDhcpServer;
import android.net.dhcp.IDhcpServerCallbacks;
+import android.net.ip.IpNeighborMonitor.NeighborEvent;
+import android.net.ip.IpNeighborMonitor.NeighborEventConsumer;
import android.net.util.InterfaceParams;
import android.net.util.InterfaceSet;
import android.net.util.SharedLog;
@@ -81,6 +91,7 @@
import org.mockito.MockitoAnnotations;
import java.net.Inet4Address;
+import java.net.InetAddress;
@RunWith(AndroidJUnit4.class)
@SmallTest
@@ -88,6 +99,8 @@
private static final String IFACE_NAME = "testnet1";
private static final String UPSTREAM_IFACE = "upstream0";
private static final String UPSTREAM_IFACE2 = "upstream1";
+ private static final int UPSTREAM_IFINDEX = 101;
+ private static final int UPSTREAM_IFINDEX2 = 102;
private static final String BLUETOOTH_IFACE_ADDR = "192.168.42.1";
private static final int BLUETOOTH_DHCP_PREFIX_LENGTH = 24;
private static final int DHCP_LEASE_TIME_SECS = 3600;
@@ -102,6 +115,7 @@
@Mock private SharedLog mSharedLog;
@Mock private IDhcpServer mDhcpServer;
@Mock private RouterAdvertisementDaemon mRaDaemon;
+ @Mock private IpNeighborMonitor mIpNeighborMonitor;
@Mock private IpServer.Dependencies mDependencies;
@Captor private ArgumentCaptor<DhcpServingParamsParcel> mDhcpParamsCaptor;
@@ -111,6 +125,7 @@
ArgumentCaptor.forClass(LinkProperties.class);
private IpServer mIpServer;
private InterfaceConfigurationParcel mInterfaceConfiguration;
+ private NeighborEventConsumer mNeighborEventConsumer;
private void initStateMachine(int interfaceType) throws Exception {
initStateMachine(interfaceType, false /* usingLegacyDhcp */);
@@ -130,16 +145,28 @@
}).when(mDependencies).makeDhcpServer(any(), mDhcpParamsCaptor.capture(), any());
when(mDependencies.getRouterAdvertisementDaemon(any())).thenReturn(mRaDaemon);
when(mDependencies.getInterfaceParams(IFACE_NAME)).thenReturn(TEST_IFACE_PARAMS);
+
+ when(mDependencies.getIfindex(eq(UPSTREAM_IFACE))).thenReturn(UPSTREAM_IFINDEX);
+ when(mDependencies.getIfindex(eq(UPSTREAM_IFACE2))).thenReturn(UPSTREAM_IFINDEX2);
+
mInterfaceConfiguration = new InterfaceConfigurationParcel();
mInterfaceConfiguration.flags = new String[0];
if (interfaceType == TETHERING_BLUETOOTH) {
mInterfaceConfiguration.ipv4Addr = BLUETOOTH_IFACE_ADDR;
mInterfaceConfiguration.prefixLength = BLUETOOTH_DHCP_PREFIX_LENGTH;
}
+
+ ArgumentCaptor<NeighborEventConsumer> neighborCaptor =
+ ArgumentCaptor.forClass(NeighborEventConsumer.class);
+ doReturn(mIpNeighborMonitor).when(mDependencies).getIpNeighborMonitor(any(), any(),
+ neighborCaptor.capture());
+
mIpServer = new IpServer(
IFACE_NAME, mLooper.getLooper(), interfaceType, mSharedLog, mNetd,
mCallback, usingLegacyDhcp, mDependencies);
mIpServer.start();
+ mNeighborEventConsumer = neighborCaptor.getValue();
+
// Starting the state machine always puts us in a consistent state and notifies
// the rest of the world that we've changed from an unknown to available state.
mLooper.dispatchAll();
@@ -158,7 +185,9 @@
initStateMachine(interfaceType, usingLegacyDhcp);
dispatchCommand(IpServer.CMD_TETHER_REQUESTED, STATE_TETHERED);
if (upstreamIface != null) {
- dispatchTetherConnectionChanged(upstreamIface);
+ LinkProperties lp = new LinkProperties();
+ lp.setInterfaceName(upstreamIface);
+ dispatchTetherConnectionChanged(upstreamIface, lp);
}
reset(mNetd, mCallback);
}
@@ -170,6 +199,8 @@
@Test
public void startsOutAvailable() {
+ when(mDependencies.getIpNeighborMonitor(any(), any(), any()))
+ .thenReturn(mIpNeighborMonitor);
mIpServer = new IpServer(IFACE_NAME, mLooper.getLooper(), TETHERING_BLUETOOTH, mSharedLog,
mNetd, mCallback, false /* usingLegacyDhcp */, mDependencies);
mIpServer.start();
@@ -467,9 +498,139 @@
verify(mDependencies, never()).makeDhcpServer(any(), any(), any());
}
+ private InetAddress addr(String addr) throws Exception {
+ return InetAddresses.parseNumericAddress(addr);
+ }
+
+ private void recvNewNeigh(int ifindex, InetAddress addr, short nudState, MacAddress mac) {
+ mNeighborEventConsumer.accept(new NeighborEvent(0, RTM_NEWNEIGH, ifindex, addr,
+ nudState, mac));
+ mLooper.dispatchAll();
+ }
+
+ private void recvDelNeigh(int ifindex, InetAddress addr, short nudState, MacAddress mac) {
+ mNeighborEventConsumer.accept(new NeighborEvent(0, RTM_DELNEIGH, ifindex, addr,
+ nudState, mac));
+ mLooper.dispatchAll();
+ }
+
+ @Test
+ public void addRemoveipv6ForwardingRules() throws Exception {
+ initTetheredStateMachine(TETHERING_WIFI, UPSTREAM_IFACE, false /* usingLegacyDhcp */);
+
+ final int myIfindex = TEST_IFACE_PARAMS.index;
+ final int notMyIfindex = myIfindex - 1;
+
+ final MacAddress myMac = TEST_IFACE_PARAMS.macAddr;
+ final InetAddress neighA = InetAddresses.parseNumericAddress("2001:db8::1");
+ final InetAddress neighB = InetAddresses.parseNumericAddress("2001:db8::2");
+ final InetAddress neighLL = InetAddresses.parseNumericAddress("fe80::1");
+ final InetAddress neighMC = InetAddresses.parseNumericAddress("ff02::1234");
+ final MacAddress macA = MacAddress.fromString("00:00:00:00:00:0a");
+ final MacAddress macB = MacAddress.fromString("11:22:33:00:00:0b");
+
+ reset(mNetd);
+
+ // Events on other interfaces are ignored.
+ recvNewNeigh(notMyIfindex, neighA, NUD_REACHABLE, macA);
+ verifyNoMoreInteractions(mNetd);
+
+ // Events on this interface are received and sent to netd.
+ recvNewNeigh(myIfindex, neighA, NUD_REACHABLE, macA);
+ verify(mNetd).tetherRuleAddDownstreamIpv6(eq(myIfindex), eq(UPSTREAM_IFINDEX),
+ eq(neighA.getAddress()), eq(myMac.toByteArray()), eq(macA.toByteArray()));
+ reset(mNetd);
+
+ recvNewNeigh(myIfindex, neighB, NUD_REACHABLE, macB);
+ verify(mNetd).tetherRuleAddDownstreamIpv6(eq(myIfindex), eq(UPSTREAM_IFINDEX),
+ eq(neighB.getAddress()), eq(myMac.toByteArray()), eq(macB.toByteArray()));
+ reset(mNetd);
+
+ // Link-local and multicast neighbors are ignored.
+ recvNewNeigh(myIfindex, neighLL, NUD_REACHABLE, macA);
+ verifyNoMoreInteractions(mNetd);
+ recvNewNeigh(myIfindex, neighMC, NUD_REACHABLE, macA);
+ verifyNoMoreInteractions(mNetd);
+
+ // A neighbor that is no longer valid causes the rule to be removed.
+ recvNewNeigh(myIfindex, neighA, NUD_FAILED, macA);
+ verify(mNetd).tetherRuleRemoveDownstreamIpv6(eq(UPSTREAM_IFINDEX), eq(neighA.getAddress()));
+ reset(mNetd);
+
+ // A neighbor that is deleted causes the rule to be removed.
+ recvDelNeigh(myIfindex, neighB, NUD_STALE, macB);
+ verify(mNetd).tetherRuleRemoveDownstreamIpv6(eq(UPSTREAM_IFINDEX), eq(neighB.getAddress()));
+ reset(mNetd);
+
+ // Upstream changes result in deleting and re-adding the rules.
+ recvNewNeigh(myIfindex, neighA, NUD_REACHABLE, macA);
+ recvNewNeigh(myIfindex, neighB, NUD_REACHABLE, macB);
+ reset(mNetd);
+
+ InOrder inOrder = inOrder(mNetd);
+ LinkProperties lp = new LinkProperties();
+ lp.setInterfaceName(UPSTREAM_IFACE2);
+ dispatchTetherConnectionChanged(UPSTREAM_IFACE2, lp);
+ inOrder.verify(mNetd).tetherRuleAddDownstreamIpv6(eq(myIfindex), eq(UPSTREAM_IFINDEX2),
+ eq(neighA.getAddress()), eq(myMac.toByteArray()), eq(macA.toByteArray()));
+ inOrder.verify(mNetd).tetherRuleRemoveDownstreamIpv6(eq(UPSTREAM_IFINDEX),
+ eq(neighA.getAddress()));
+ inOrder.verify(mNetd).tetherRuleAddDownstreamIpv6(eq(myIfindex), eq(UPSTREAM_IFINDEX2),
+ eq(neighB.getAddress()), eq(myMac.toByteArray()), eq(macB.toByteArray()));
+ inOrder.verify(mNetd).tetherRuleRemoveDownstreamIpv6(eq(UPSTREAM_IFINDEX),
+ eq(neighB.getAddress()));
+ reset(mNetd);
+
+ // When the upstream is lost, rules are removed.
+ dispatchTetherConnectionChanged(null, null);
+ verify(mNetd).tetherRuleRemoveDownstreamIpv6(eq(UPSTREAM_IFINDEX2),
+ eq(neighA.getAddress()));
+ verify(mNetd).tetherRuleRemoveDownstreamIpv6(eq(UPSTREAM_IFINDEX2),
+ eq(neighB.getAddress()));
+ reset(mNetd);
+
+ // If the upstream is IPv4-only, no rules are added.
+ dispatchTetherConnectionChanged(UPSTREAM_IFACE);
+ reset(mNetd);
+ recvNewNeigh(myIfindex, neighA, NUD_REACHABLE, macA);
+ verifyNoMoreInteractions(mNetd);
+
+ // Rules can be added again once upstream IPv6 connectivity is available.
+ lp.setInterfaceName(UPSTREAM_IFACE);
+ dispatchTetherConnectionChanged(UPSTREAM_IFACE, lp);
+ recvNewNeigh(myIfindex, neighB, NUD_REACHABLE, macB);
+ verify(mNetd).tetherRuleAddDownstreamIpv6(eq(myIfindex), eq(UPSTREAM_IFINDEX),
+ eq(neighB.getAddress()), eq(myMac.toByteArray()), eq(macB.toByteArray()));
+ verify(mNetd, never()).tetherRuleAddDownstreamIpv6(anyInt(), anyInt(),
+ eq(neighA.getAddress()), any(), any());
+
+ // If upstream IPv6 connectivity is lost, rules are removed.
+ reset(mNetd);
+ dispatchTetherConnectionChanged(UPSTREAM_IFACE, null);
+ verify(mNetd).tetherRuleRemoveDownstreamIpv6(eq(UPSTREAM_IFINDEX), eq(neighB.getAddress()));
+
+ // When the interface goes down, rules are removed.
+ lp.setInterfaceName(UPSTREAM_IFACE);
+ dispatchTetherConnectionChanged(UPSTREAM_IFACE, lp);
+ recvNewNeigh(myIfindex, neighA, NUD_REACHABLE, macA);
+ recvNewNeigh(myIfindex, neighB, NUD_REACHABLE, macB);
+ verify(mNetd).tetherRuleAddDownstreamIpv6(eq(myIfindex), eq(UPSTREAM_IFINDEX),
+ eq(neighA.getAddress()), eq(myMac.toByteArray()), eq(macA.toByteArray()));
+ verify(mNetd).tetherRuleAddDownstreamIpv6(eq(myIfindex), eq(UPSTREAM_IFINDEX),
+ eq(neighB.getAddress()), eq(myMac.toByteArray()), eq(macB.toByteArray()));
+ reset(mNetd);
+
+ mIpServer.stop();
+ mLooper.dispatchAll();
+ verify(mNetd).tetherRuleRemoveDownstreamIpv6(eq(UPSTREAM_IFINDEX), eq(neighA.getAddress()));
+ verify(mNetd).tetherRuleRemoveDownstreamIpv6(eq(UPSTREAM_IFINDEX), eq(neighB.getAddress()));
+ reset(mNetd);
+ }
+
private void assertDhcpStarted(IpPrefix expectedPrefix) throws Exception {
verify(mDependencies, times(1)).makeDhcpServer(eq(IFACE_NAME), any(), any());
- verify(mDhcpServer, timeout(MAKE_DHCPSERVER_TIMEOUT_MS).times(1)).start(any());
+ verify(mDhcpServer, timeout(MAKE_DHCPSERVER_TIMEOUT_MS).times(1)).startWithCallbacks(
+ any(), any());
final DhcpServingParamsParcel params = mDhcpParamsCaptor.getValue();
// Last address byte is random
assertTrue(expectedPrefix.contains(intToInet4AddressHTH(params.serverAddr)));
@@ -507,10 +668,17 @@
*
* @see #dispatchCommand(int)
* @param upstreamIface String name of upstream interface (or null)
+ * @param v6lp IPv6 LinkProperties of the upstream interface, or null for an IPv4-only upstream.
*/
+ private void dispatchTetherConnectionChanged(String upstreamIface, LinkProperties v6lp) {
+ dispatchTetherConnectionChanged(upstreamIface);
+ mIpServer.sendMessage(IpServer.CMD_IPV6_TETHER_UPDATE, v6lp);
+ mLooper.dispatchAll();
+ }
+
private void dispatchTetherConnectionChanged(String upstreamIface) {
- mIpServer.sendMessage(IpServer.CMD_TETHER_CONNECTION_CHANGED,
- new InterfaceSet(upstreamIface));
+ final InterfaceSet ifs = (upstreamIface != null) ? new InterfaceSet(upstreamIface) : null;
+ mIpServer.sendMessage(IpServer.CMD_TETHER_CONNECTION_CHANGED, ifs);
mLooper.dispatchAll();
}
diff --git a/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/ConnectedClientsTrackerTest.kt b/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/ConnectedClientsTrackerTest.kt
new file mode 100644
index 0000000..56f3e21
--- /dev/null
+++ b/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/ConnectedClientsTrackerTest.kt
@@ -0,0 +1,157 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.connectivity.tethering
+
+import android.net.LinkAddress
+import android.net.MacAddress
+import android.net.TetheredClient
+import android.net.TetheredClient.AddressInfo
+import android.net.TetheringManager.TETHERING_USB
+import android.net.TetheringManager.TETHERING_WIFI
+import android.net.ip.IpServer
+import android.net.wifi.WifiClient
+import androidx.test.filters.SmallTest
+import androidx.test.runner.AndroidJUnit4
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mockito.doReturn
+import org.mockito.Mockito.mock
+import kotlin.test.assertEquals
+import kotlin.test.assertFalse
+import kotlin.test.assertTrue
+
+@RunWith(AndroidJUnit4::class)
+@SmallTest
+class ConnectedClientsTrackerTest {
+
+ private val server1 = mock(IpServer::class.java)
+ private val server2 = mock(IpServer::class.java)
+ private val servers = listOf(server1, server2)
+
+ private val clock = TestClock(1324L)
+
+ private val client1Addr = MacAddress.fromString("01:23:45:67:89:0A")
+ private val client1 = TetheredClient(client1Addr, listOf(
+ AddressInfo(LinkAddress("192.168.43.44/32"), null /* hostname */, clock.time + 20)),
+ TETHERING_WIFI)
+ private val wifiClient1 = makeWifiClient(client1Addr)
+ private val client2Addr = MacAddress.fromString("02:34:56:78:90:AB")
+ private val client2Exp30AddrInfo = AddressInfo(
+ LinkAddress("192.168.43.45/32"), "my_hostname", clock.time + 30)
+ private val client2 = TetheredClient(client2Addr, listOf(
+ client2Exp30AddrInfo,
+ AddressInfo(LinkAddress("2001:db8:12::34/72"), "other_hostname", clock.time + 10)),
+ TETHERING_WIFI)
+ private val wifiClient2 = makeWifiClient(client2Addr)
+ private val client3Addr = MacAddress.fromString("03:45:67:89:0A:BC")
+ private val client3 = TetheredClient(client3Addr,
+ listOf(AddressInfo(LinkAddress("2001:db8:34::34/72"), "other_other_hostname",
+ clock.time + 10)),
+ TETHERING_USB)
+
+ @Test
+ fun testUpdateConnectedClients() {
+ doReturn(emptyList<TetheredClient>()).`when`(server1).allLeases
+ doReturn(emptyList<TetheredClient>()).`when`(server2).allLeases
+
+ val tracker = ConnectedClientsTracker(clock)
+ assertFalse(tracker.updateConnectedClients(servers, null))
+
+ // Obtain a lease for client 1
+ doReturn(listOf(client1)).`when`(server1).allLeases
+ assertSameClients(listOf(client1), assertNewClients(tracker, servers, listOf(wifiClient1)))
+
+ // Client 2 L2-connected, no lease yet
+ val client2WithoutAddr = TetheredClient(client2Addr, emptyList(), TETHERING_WIFI)
+ assertSameClients(listOf(client1, client2WithoutAddr),
+ assertNewClients(tracker, servers, listOf(wifiClient1, wifiClient2)))
+
+ // Client 2 lease obtained
+ doReturn(listOf(client1, client2)).`when`(server1).allLeases
+ assertSameClients(listOf(client1, client2), assertNewClients(tracker, servers, null))
+
+ // Client 3 lease obtained
+ doReturn(listOf(client3)).`when`(server2).allLeases
+ assertSameClients(listOf(client1, client2, client3),
+ assertNewClients(tracker, servers, null))
+
+ // Client 2 L2-disconnected
+ assertSameClients(listOf(client1, client3),
+ assertNewClients(tracker, servers, listOf(wifiClient1)))
+
+ // Client 1 L2-disconnected
+ assertSameClients(listOf(client3), assertNewClients(tracker, servers, emptyList()))
+
+ // Client 1 comes back
+ assertSameClients(listOf(client1, client3),
+ assertNewClients(tracker, servers, listOf(wifiClient1)))
+
+ // Leases lost, client 1 still L2-connected
+ doReturn(emptyList<TetheredClient>()).`when`(server1).allLeases
+ doReturn(emptyList<TetheredClient>()).`when`(server2).allLeases
+ assertSameClients(listOf(TetheredClient(client1Addr, emptyList(), TETHERING_WIFI)),
+ assertNewClients(tracker, servers, null))
+ }
+
+ @Test
+ fun testUpdateConnectedClients_LeaseExpiration() {
+ val tracker = ConnectedClientsTracker(clock)
+ doReturn(listOf(client1, client2)).`when`(server1).allLeases
+ doReturn(listOf(client3)).`when`(server2).allLeases
+ assertSameClients(listOf(client1, client2, client3), assertNewClients(
+ tracker, servers, listOf(wifiClient1, wifiClient2)))
+
+ clock.time += 20
+ // Client 3 has no remaining lease: removed
+ val expectedClients = listOf(
+ // Client 1 has no remaining lease but is L2-connected
+ TetheredClient(client1Addr, emptyList(), TETHERING_WIFI),
+ // Client 2 has some expired leases
+ TetheredClient(
+ client2Addr,
+ // Only the "t + 30" address is left, the "t + 10" address expired
+ listOf(client2Exp30AddrInfo),
+ TETHERING_WIFI))
+ assertSameClients(expectedClients, assertNewClients(tracker, servers, null))
+ }
+
+ private fun assertNewClients(
+ tracker: ConnectedClientsTracker,
+ ipServers: Iterable<IpServer>,
+ wifiClients: List<WifiClient>?
+ ): List<TetheredClient> {
+ assertTrue(tracker.updateConnectedClients(ipServers, wifiClients))
+ return tracker.lastTetheredClients
+ }
+
+ private fun assertSameClients(expected: List<TetheredClient>, actual: List<TetheredClient>) {
+ val expectedSet = HashSet(expected)
+ assertEquals(expected.size, expectedSet.size)
+ assertEquals(expectedSet, HashSet(actual))
+ }
+
+ private fun makeWifiClient(macAddr: MacAddress): WifiClient {
+ // Use a mock WifiClient as the constructor is not part of the WiFi module exported API.
+ return mock(WifiClient::class.java).apply { doReturn(macAddr).`when`(this).macAddress }
+ }
+
+ private class TestClock(var time: Long) : ConnectedClientsTracker.Clock() {
+ override fun elapsedRealtime(): Long {
+ return time
+ }
+ }
+}
\ No newline at end of file
diff --git a/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java b/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java
index a917849..4581b56 100644
--- a/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java
+++ b/packages/Tethering/tests/unit/src/com/android/server/connectivity/tethering/TetheringTest.java
@@ -28,6 +28,7 @@
import static android.net.TetheringManager.EXTRA_ACTIVE_LOCAL_ONLY;
import static android.net.TetheringManager.EXTRA_ACTIVE_TETHER;
import static android.net.TetheringManager.EXTRA_AVAILABLE_TETHER;
+import static android.net.TetheringManager.TETHERING_ETHERNET;
import static android.net.TetheringManager.TETHERING_NCM;
import static android.net.TetheringManager.TETHERING_USB;
import static android.net.TetheringManager.TETHERING_WIFI;
@@ -75,6 +76,8 @@
import android.content.res.Resources;
import android.hardware.usb.UsbManager;
import android.net.ConnectivityManager;
+import android.net.EthernetManager;
+import android.net.EthernetManager.TetheredInterfaceRequest;
import android.net.INetd;
import android.net.ITetheringEventCallback;
import android.net.InetAddresses;
@@ -88,12 +91,14 @@
import android.net.NetworkRequest;
import android.net.RouteInfo;
import android.net.TetherStatesParcel;
+import android.net.TetheredClient;
import android.net.TetheringCallbackStartedParcel;
import android.net.TetheringConfigurationParcel;
import android.net.TetheringRequestParcel;
import android.net.dhcp.DhcpServerCallbacks;
import android.net.dhcp.DhcpServingParamsParcel;
import android.net.dhcp.IDhcpServer;
+import android.net.ip.IpNeighborMonitor;
import android.net.ip.IpServer;
import android.net.ip.RouterAdvertisementDaemon;
import android.net.util.InterfaceParams;
@@ -142,6 +147,7 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
+import java.util.List;
import java.util.Vector;
@RunWith(AndroidJUnit4.class)
@@ -171,11 +177,13 @@
@Mock private UpstreamNetworkMonitor mUpstreamNetworkMonitor;
@Mock private IPv6TetheringCoordinator mIPv6TetheringCoordinator;
@Mock private RouterAdvertisementDaemon mRouterAdvertisementDaemon;
+ @Mock private IpNeighborMonitor mIpNeighborMonitor;
@Mock private IDhcpServer mDhcpServer;
@Mock private INetd mNetd;
@Mock private UserManager mUserManager;
@Mock private NetworkRequest mNetworkRequest;
@Mock private ConnectivityManager mCm;
+ @Mock private EthernetManager mEm;
private final MockIpServerDependencies mIpServerDependencies =
spy(new MockIpServerDependencies());
@@ -228,6 +236,7 @@
if (Context.USER_SERVICE.equals(name)) return mUserManager;
if (Context.NETWORK_STATS_SERVICE.equals(name)) return mStatsManager;
if (Context.CONNECTIVITY_SERVICE.equals(name)) return mCm;
+ if (Context.ETHERNET_SERVICE.equals(name)) return mEm;
return super.getSystemService(name);
}
@@ -276,6 +285,11 @@
}
}).run();
}
+
+ public IpNeighborMonitor getIpNeighborMonitor(Handler h, SharedLog l,
+ IpNeighborMonitor.NeighborEventConsumer c) {
+ return mIpNeighborMonitor;
+ }
}
private class MockTetheringConfiguration extends TetheringConfiguration {
@@ -470,6 +484,7 @@
ArgumentCaptor.forClass(PhoneStateListener.class);
verify(mTelephonyManager).listen(phoneListenerCaptor.capture(),
eq(PhoneStateListener.LISTEN_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGE));
+ verify(mWifiManager).registerSoftApCallback(any(), any());
mPhoneStateListener = phoneListenerCaptor.getValue();
}
@@ -728,7 +743,8 @@
sendIPv6TetherUpdates(upstreamState);
verify(mRouterAdvertisementDaemon, never()).buildNewRa(any(), notNull());
- verify(mDhcpServer, timeout(DHCPSERVER_START_TIMEOUT_MS).times(1)).start(any());
+ verify(mDhcpServer, timeout(DHCPSERVER_START_TIMEOUT_MS).times(1)).startWithCallbacks(
+ any(), any());
}
@Test
@@ -764,7 +780,8 @@
verify(mNetd, times(1)).tetherAddForward(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
verify(mNetd, times(1)).ipfwdAddInterfaceForward(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
verify(mRouterAdvertisementDaemon, times(1)).start();
- verify(mDhcpServer, timeout(DHCPSERVER_START_TIMEOUT_MS).times(1)).start(any());
+ verify(mDhcpServer, timeout(DHCPSERVER_START_TIMEOUT_MS).times(1)).startWithCallbacks(
+ any(), any());
sendIPv6TetherUpdates(upstreamState);
verify(mRouterAdvertisementDaemon, times(1)).buildNewRa(any(), notNull());
@@ -778,7 +795,8 @@
verify(mNetd, times(1)).tetherAddForward(TEST_USB_IFNAME, TEST_XLAT_MOBILE_IFNAME);
verify(mNetd, times(1)).tetherAddForward(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
- verify(mDhcpServer, timeout(DHCPSERVER_START_TIMEOUT_MS).times(1)).start(any());
+ verify(mDhcpServer, timeout(DHCPSERVER_START_TIMEOUT_MS).times(1)).startWithCallbacks(
+ any(), any());
verify(mNetd, times(1)).ipfwdAddInterfaceForward(TEST_USB_IFNAME, TEST_XLAT_MOBILE_IFNAME);
verify(mNetd, times(1)).ipfwdAddInterfaceForward(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
@@ -794,7 +812,8 @@
runUsbTethering(upstreamState);
verify(mNetd, times(1)).tetherAddForward(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
- verify(mDhcpServer, timeout(DHCPSERVER_START_TIMEOUT_MS).times(1)).start(any());
+ verify(mDhcpServer, timeout(DHCPSERVER_START_TIMEOUT_MS).times(1)).startWithCallbacks(
+ any(), any());
verify(mNetd, times(1)).ipfwdAddInterfaceForward(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
// Then 464xlat comes up
@@ -817,7 +836,8 @@
verify(mNetd, times(1)).tetherAddForward(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
verify(mNetd, times(1)).ipfwdAddInterfaceForward(TEST_USB_IFNAME, TEST_MOBILE_IFNAME);
// DHCP not restarted on downstream (still times(1))
- verify(mDhcpServer, timeout(DHCPSERVER_START_TIMEOUT_MS).times(1)).start(any());
+ verify(mDhcpServer, timeout(DHCPSERVER_START_TIMEOUT_MS).times(1)).startWithCallbacks(
+ any(), any());
}
@Test
@@ -847,7 +867,8 @@
public void workingNcmTethering() throws Exception {
runNcmTethering();
- verify(mDhcpServer, timeout(DHCPSERVER_START_TIMEOUT_MS).times(1)).start(any());
+ verify(mDhcpServer, timeout(DHCPSERVER_START_TIMEOUT_MS).times(1)).startWithCallbacks(
+ any(), any());
}
@Test
@@ -1171,6 +1192,11 @@
}
@Override
+ public void onTetherClientsChanged(List<TetheredClient> clients) {
+ // TODO: check this
+ }
+
+ @Override
public void onCallbackStarted(TetheringCallbackStartedParcel parcel) {
mActualUpstreams.add(parcel.upstreamNetwork);
mTetheringConfigs.add(parcel.config);
@@ -1295,6 +1321,24 @@
assertEquals(fakeSubId, newConfig.activeDataSubId);
}
+ @Test
+ public void testNoDuplicatedEthernetRequest() throws Exception {
+ final TetheredInterfaceRequest mockRequest = mock(TetheredInterfaceRequest.class);
+ when(mEm.requestTetheredInterface(any(), any())).thenReturn(mockRequest);
+ mTethering.startTethering(createTetheringRquestParcel(TETHERING_ETHERNET), null);
+ mLooper.dispatchAll();
+ verify(mEm, times(1)).requestTetheredInterface(any(), any());
+ mTethering.startTethering(createTetheringRquestParcel(TETHERING_ETHERNET), null);
+ mLooper.dispatchAll();
+ verifyNoMoreInteractions(mEm);
+ mTethering.stopTethering(TETHERING_ETHERNET);
+ mLooper.dispatchAll();
+ verify(mockRequest, times(1)).release();
+ mTethering.stopTethering(TETHERING_ETHERNET);
+ mLooper.dispatchAll();
+ verifyNoMoreInteractions(mEm);
+ }
+
private void workingWifiP2pGroupOwner(
boolean emulateInterfaceStatusChanged) throws Exception {
if (emulateInterfaceStatusChanged) {
diff --git a/proto/src/system_messages.proto b/proto/src/system_messages.proto
index ef071a4..2c7ea310 100644
--- a/proto/src/system_messages.proto
+++ b/proto/src/system_messages.proto
@@ -230,6 +230,10 @@
// Package: android
NOTE_TEST_HARNESS_MODE_ENABLED = 54;
+ // Display the Android Debug Protocol status
+ // Package: android
+ NOTE_ADB_WIFI_ACTIVE = 62;
+
// ADD_NEW_IDS_ABOVE_THIS_LINE
// Legacy IDs with arbitrary values appear below
// Legacy IDs existed as stable non-conflicting constants prior to the O release
diff --git a/services/Android.bp b/services/Android.bp
index 073fccc..5019bb1 100644
--- a/services/Android.bp
+++ b/services/Android.bp
@@ -72,7 +72,7 @@
libs: [
"android.hidl.manager-V1.0-java",
- "framework-tethering"
+ "framework-tethering-stubs",
],
plugins: [
@@ -110,22 +110,30 @@
name: "services-stubs.sources",
srcs: [":services-sources"],
installable: false,
- // TODO: remove the --hide options below
args: " --show-annotation android.annotation.SystemApi\\(client=android.annotation.SystemApi.Client.SYSTEM_SERVER\\)" +
" --hide-annotation android.annotation.Hide" +
+ " --hide InternalClasses" + // com.android.* classes are okay in this interface
+ // TODO: remove the --hide options below
" --hide-package com.google.android.startop.iorap" +
" --hide ReferencesHidden" +
" --hide DeprecationMismatch" +
" --hide HiddenTypedefConstant",
- libs: [
- "framework-all",
- ],
visibility: ["//visibility:private"],
check_api: {
current: {
api_file: "api/current.txt",
removed_api_file: "api/removed.txt",
},
+ last_released: {
+ api_file: ":last-released-system-server-api",
+ removed_api_file: "api/removed.txt",
+ baseline_file: ":system-server-api-incompatibilities-with-last-released"
+ },
+ api_lint: {
+ enabled: true,
+ new_since: ":last-released-system-server-api",
+ baseline_file: "api/lint-baseline.txt",
+ },
},
}
diff --git a/services/accessibility/OWNERS b/services/accessibility/OWNERS
index 265674a..c6f42f7 100644
--- a/services/accessibility/OWNERS
+++ b/services/accessibility/OWNERS
@@ -1,3 +1,4 @@
svetoslavganov@google.com
pweaver@google.com
rhedjao@google.com
+qasid@google.com
diff --git a/services/api/lint-baseline.txt b/services/api/lint-baseline.txt
new file mode 100644
index 0000000..9a97707
--- /dev/null
+++ b/services/api/lint-baseline.txt
@@ -0,0 +1 @@
+// Baseline format: 1.0
diff --git a/services/core/Android.bp b/services/core/Android.bp
index 649b5ea..123c5d5 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -29,7 +29,8 @@
"android.hardware.tv.cec-V1.0-java",
"android.hardware.vibrator-java",
"app-compat-annotations",
- "framework-tethering",
+ "framework-tethering-stubs",
+ "ike-stubs",
],
required: [
@@ -43,6 +44,7 @@
"android.hardware.broadcastradio-V2.0-java",
"android.hardware.health-V1.0-java",
"android.hardware.health-V2.0-java",
+ "android.hardware.health-V2.1-java",
"android.hardware.light-java",
"android.hardware.weaver-V1.0-java",
"android.hardware.biometrics.face-V1.0-java",
@@ -54,6 +56,10 @@
"dnsresolver_aidl_interface-V2-java",
"netd_event_listener_interface-java",
],
+
+ plugins: [
+ "compat-changeid-annotation-processor",
+ ],
}
java_genrule {
diff --git a/services/core/java/com/android/server/AlarmManagerService.java b/services/core/java/com/android/server/AlarmManagerService.java
index bbb7c52..ada1182 100644
--- a/services/core/java/com/android/server/AlarmManagerService.java
+++ b/services/core/java/com/android/server/AlarmManagerService.java
@@ -1675,6 +1675,7 @@
Intent intent = new Intent(Intent.ACTION_TIMEZONE_CHANGED);
intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING
| Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND
+ | Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT
| Intent.FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS);
intent.putExtra("time-zone", zone.getID());
getContext().sendBroadcastAsUser(intent, UserHandle.ALL);
diff --git a/services/core/java/com/android/server/BatteryService.java b/services/core/java/com/android/server/BatteryService.java
index 0816955..b676f99 100644
--- a/services/core/java/com/android/server/BatteryService.java
+++ b/services/core/java/com/android/server/BatteryService.java
@@ -26,8 +26,10 @@
import android.database.ContentObserver;
import android.hardware.health.V1_0.HealthInfo;
import android.hardware.health.V2_0.IHealth;
-import android.hardware.health.V2_0.IHealthInfoCallback;
import android.hardware.health.V2_0.Result;
+import android.hardware.health.V2_1.BatteryCapacityLevel;
+import android.hardware.health.V2_1.Constants;
+import android.hardware.health.V2_1.IHealthInfoCallback;
import android.hidl.manager.V1_0.IServiceManager;
import android.hidl.manager.V1_0.IServiceNotification;
import android.metrics.LogMaker;
@@ -144,6 +146,7 @@
private HealthInfo mHealthInfo;
private final HealthInfo mLastHealthInfo = new HealthInfo();
+ private android.hardware.health.V2_1.HealthInfo mHealthInfo2p1;
private boolean mBatteryLevelCritical;
private int mLastBatteryStatus;
private int mLastBatteryHealth;
@@ -358,6 +361,9 @@
}
private boolean shouldShutdownLocked() {
+ if (mHealthInfo2p1.batteryCapacityLevel != BatteryCapacityLevel.UNSUPPORTED) {
+ return (mHealthInfo2p1.batteryCapacityLevel == BatteryCapacityLevel.CRITICAL);
+ }
if (mHealthInfo.batteryLevel > 0) {
return false;
}
@@ -415,22 +421,23 @@
}
}
- private void update(android.hardware.health.V2_0.HealthInfo info) {
+ private void update(android.hardware.health.V2_1.HealthInfo info) {
traceBegin("HealthInfoUpdate");
Trace.traceCounter(Trace.TRACE_TAG_POWER, "BatteryChargeCounter",
- info.legacy.batteryChargeCounter);
+ info.legacy.legacy.batteryChargeCounter);
Trace.traceCounter(Trace.TRACE_TAG_POWER, "BatteryCurrent",
- info.legacy.batteryCurrent);
+ info.legacy.legacy.batteryCurrent);
synchronized (mLock) {
if (!mUpdatesStopped) {
- mHealthInfo = info.legacy;
+ mHealthInfo = info.legacy.legacy;
+ mHealthInfo2p1 = info;
// Process the new values.
processValuesLocked(false);
mLock.notifyAll(); // for any waiters on new info
} else {
- copy(mLastHealthInfo, info.legacy);
+ copy(mLastHealthInfo, info.legacy.legacy);
}
}
traceEnd();
@@ -484,7 +491,8 @@
mBatteryStats.setBatteryState(mHealthInfo.batteryStatus, mHealthInfo.batteryHealth,
mPlugType, mHealthInfo.batteryLevel, mHealthInfo.batteryTemperature,
mHealthInfo.batteryVoltage, mHealthInfo.batteryChargeCounter,
- mHealthInfo.batteryFullCharge);
+ mHealthInfo.batteryFullCharge,
+ mHealthInfo2p1.batteryChargeTimeToFullNowSeconds);
} catch (RemoteException e) {
// Should never happen.
}
@@ -1120,8 +1128,21 @@
private final class HealthHalCallback extends IHealthInfoCallback.Stub
implements HealthServiceWrapper.Callback {
@Override public void healthInfoChanged(android.hardware.health.V2_0.HealthInfo props) {
+ android.hardware.health.V2_1.HealthInfo propsLatest =
+ new android.hardware.health.V2_1.HealthInfo();
+ propsLatest.legacy = props;
+
+ propsLatest.batteryCapacityLevel = BatteryCapacityLevel.UNSUPPORTED;
+ propsLatest.batteryChargeTimeToFullNowSeconds =
+ Constants.BATTERY_CHARGE_TIME_TO_FULL_NOW_SECONDS_UNSUPPORTED;
+
+ BatteryService.this.update(propsLatest);
+ }
+
+ @Override public void healthInfoChanged_2_1(android.hardware.health.V2_1.HealthInfo props) {
BatteryService.this.update(props);
}
+
// on new service registered
@Override public void onRegistration(IHealth oldService, IHealth newService,
String instance) {
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 450794b..fcdb8a9 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -1536,7 +1536,8 @@
}
@Override
- public NetworkCapabilities[] getDefaultNetworkCapabilitiesForUser(int userId) {
+ public NetworkCapabilities[] getDefaultNetworkCapabilitiesForUser(
+ int userId, String callingPackageName) {
// The basic principle is: if an app's traffic could possibly go over a
// network, without the app doing anything multinetwork-specific,
// (hence, by "default"), then include that network's capabilities in
@@ -1558,7 +1559,10 @@
NetworkAgentInfo nai = getDefaultNetwork();
NetworkCapabilities nc = getNetworkCapabilitiesInternal(nai);
if (nc != null) {
- result.put(nai.network, nc);
+ result.put(
+ nai.network,
+ maybeSanitizeLocationInfoForCaller(
+ nc, Binder.getCallingUid(), callingPackageName));
}
synchronized (mVpns) {
@@ -1568,10 +1572,12 @@
Network[] networks = vpn.getUnderlyingNetworks();
if (networks != null) {
for (Network network : networks) {
- nai = getNetworkAgentInfoForNetwork(network);
- nc = getNetworkCapabilitiesInternal(nai);
+ nc = getNetworkCapabilitiesInternal(network);
if (nc != null) {
- result.put(network, nc);
+ result.put(
+ network,
+ maybeSanitizeLocationInfoForCaller(
+ nc, Binder.getCallingUid(), callingPackageName));
}
}
}
@@ -1638,20 +1644,26 @@
}
}
+ private NetworkCapabilities getNetworkCapabilitiesInternal(Network network) {
+ return getNetworkCapabilitiesInternal(getNetworkAgentInfoForNetwork(network));
+ }
+
private NetworkCapabilities getNetworkCapabilitiesInternal(NetworkAgentInfo nai) {
if (nai == null) return null;
synchronized (nai) {
if (nai.networkCapabilities == null) return null;
return networkCapabilitiesRestrictedForCallerPermissions(
- nai.networkCapabilities,
- Binder.getCallingPid(), Binder.getCallingUid());
+ nai.networkCapabilities, Binder.getCallingPid(), Binder.getCallingUid());
}
}
@Override
- public NetworkCapabilities getNetworkCapabilities(Network network) {
+ public NetworkCapabilities getNetworkCapabilities(Network network, String callingPackageName) {
+ mAppOpsManager.checkPackage(Binder.getCallingUid(), callingPackageName);
enforceAccessPermission();
- return getNetworkCapabilitiesInternal(getNetworkAgentInfoForNetwork(network));
+ return maybeSanitizeLocationInfoForCaller(
+ getNetworkCapabilitiesInternal(network),
+ Binder.getCallingUid(), callingPackageName);
}
@VisibleForTesting
@@ -1667,20 +1679,34 @@
}
newNc.setAdministratorUids(Collections.EMPTY_LIST);
- maybeSanitizeLocationInfoForCaller(newNc, callerUid);
-
return newNc;
}
- private void maybeSanitizeLocationInfoForCaller(
- NetworkCapabilities nc, int callerUid) {
- // TODO(b/142072839): Conditionally reset the owner UID if the following
- // conditions are not met:
- // 1. The destination app is the network owner
- // 2. The destination app has the ACCESS_COARSE_LOCATION permission granted
- // if target SDK<29 or otherwise has the ACCESS_FINE_LOCATION permission granted
- // 3. The user's location toggle is on
- nc.setOwnerUid(INVALID_UID);
+ @VisibleForTesting
+ @Nullable
+ NetworkCapabilities maybeSanitizeLocationInfoForCaller(
+ @Nullable NetworkCapabilities nc, int callerUid, @NonNull String callerPkgName) {
+ if (nc == null) {
+ return null;
+ }
+ final NetworkCapabilities newNc = new NetworkCapabilities(nc);
+ if (callerUid != newNc.getOwnerUid()) {
+ newNc.setOwnerUid(INVALID_UID);
+ return newNc;
+ }
+
+ Binder.withCleanCallingIdentity(
+ () -> {
+ if (!mLocationPermissionChecker.checkLocationPermission(
+ callerPkgName, null /* featureId */, callerUid, null /* message */)) {
+ // Caller does not have the requisite location permissions. Reset the
+ // owner's UID in the NetworkCapabilities.
+ newNc.setOwnerUid(INVALID_UID);
+ }
+ }
+ );
+
+ return newNc;
}
private LinkProperties linkPropertiesRestrictedForCallerPermissions(
@@ -1755,7 +1781,7 @@
public boolean isActiveNetworkMetered() {
enforceAccessPermission();
- final NetworkCapabilities caps = getNetworkCapabilities(getActiveNetwork());
+ final NetworkCapabilities caps = getNetworkCapabilitiesInternal(getActiveNetwork());
if (caps != null) {
return !caps.hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED);
} else {
@@ -3269,7 +3295,6 @@
for (int i = 0; i < nai.numNetworkRequests(); i++) {
NetworkRequest request = nai.requestAt(i);
final NetworkRequestInfo nri = mNetworkRequests.get(request);
- ensureRunningOnConnectivityServiceThread();
final NetworkAgentInfo currentNetwork = nri.mSatisfier;
if (currentNetwork != null && currentNetwork.network.netId == nai.network.netId) {
nri.mSatisfier = null;
@@ -3421,7 +3446,6 @@
// If this Network is already the highest scoring Network for a request, or if
// there is hope for it to become one if it validated, then it is needed.
- ensureRunningOnConnectivityServiceThread();
if (nri.request.isRequest() && nai.satisfies(nri.request) &&
(nai.isSatisfyingRequest(nri.request.requestId) ||
// Note that this catches two important cases:
@@ -3460,7 +3484,6 @@
if (mNetworkRequests.get(nri.request) == null) {
return;
}
- ensureRunningOnConnectivityServiceThread();
if (nri.mSatisfier != null) {
return;
}
@@ -3498,7 +3521,6 @@
mNetworkRequestInfoLogs.log("RELEASE " + nri);
if (nri.request.isRequest()) {
boolean wasKept = false;
- ensureRunningOnConnectivityServiceThread();
final NetworkAgentInfo nai = nri.mSatisfier;
if (nai != null) {
boolean wasBackgroundNetwork = nai.isBackgroundNetwork();
@@ -4783,7 +4805,7 @@
return false;
}
- return vpn.startAlwaysOnVpn();
+ return vpn.startAlwaysOnVpn(mKeyStore);
}
}
@@ -4798,7 +4820,7 @@
Slog.w(TAG, "User " + userId + " has no Vpn configuration");
return false;
}
- return vpn.isAlwaysOnPackageSupported(packageName);
+ return vpn.isAlwaysOnPackageSupported(packageName, mKeyStore);
}
}
@@ -4819,11 +4841,11 @@
Slog.w(TAG, "User " + userId + " has no Vpn configuration");
return false;
}
- if (!vpn.setAlwaysOnPackage(packageName, lockdown, lockdownWhitelist)) {
+ if (!vpn.setAlwaysOnPackage(packageName, lockdown, lockdownWhitelist, mKeyStore)) {
return false;
}
if (!startAlwaysOnVpn(userId)) {
- vpn.setAlwaysOnPackage(null, false, null);
+ vpn.setAlwaysOnPackage(null, false, null, mKeyStore);
return false;
}
}
@@ -5009,7 +5031,7 @@
loge("Starting user already has a VPN");
return;
}
- userVpn = new Vpn(mHandler.getLooper(), mContext, mNMS, userId);
+ userVpn = new Vpn(mHandler.getLooper(), mContext, mNMS, userId, mKeyStore);
mVpns.put(userId, userVpn);
if (mUserManager.getUserInfo(userId).isPrimary() && LockdownVpnTracker.isEnabled()) {
updateLockdownVpn();
@@ -5080,7 +5102,7 @@
if (TextUtils.equals(vpn.getAlwaysOnPackage(), packageName)) {
Slog.d(TAG, "Restarting always-on VPN package " + packageName + " for user "
+ userId);
- vpn.startAlwaysOnVpn();
+ vpn.startAlwaysOnVpn(mKeyStore);
}
}
}
@@ -5102,7 +5124,7 @@
if (TextUtils.equals(vpn.getAlwaysOnPackage(), packageName) && !isReplacing) {
Slog.d(TAG, "Removing always-on VPN package " + packageName + " for user "
+ userId);
- vpn.setAlwaysOnPackage(null, false, null);
+ vpn.setAlwaysOnPackage(null, false, null, mKeyStore);
}
}
}
@@ -5322,8 +5344,8 @@
}
public String toString() {
- return "uid/pid:" + mUid + "/" + mPid + " " + request +
- (mPendingIntent == null ? "" : " to trigger " + mPendingIntent);
+ return "uid/pid:" + mUid + "/" + mPid + " " + request
+ + (mPendingIntent == null ? "" : " to trigger " + mPendingIntent);
}
}
@@ -6416,8 +6438,13 @@
}
switch (notificationType) {
case ConnectivityManager.CALLBACK_AVAILABLE: {
- putParcelable(bundle, networkCapabilitiesRestrictedForCallerPermissions(
- networkAgent.networkCapabilities, nri.mPid, nri.mUid));
+ final NetworkCapabilities nc =
+ networkCapabilitiesRestrictedForCallerPermissions(
+ networkAgent.networkCapabilities, nri.mPid, nri.mUid);
+ putParcelable(
+ bundle,
+ maybeSanitizeLocationInfoForCaller(
+ nc, nri.mUid, nri.request.getRequestorPackageName()));
putParcelable(bundle, linkPropertiesRestrictedForCallerPermissions(
networkAgent.linkProperties, nri.mPid, nri.mUid));
// For this notification, arg1 contains the blocked status.
@@ -6430,9 +6457,13 @@
}
case ConnectivityManager.CALLBACK_CAP_CHANGED: {
// networkAgent can't be null as it has been accessed a few lines above.
- final NetworkCapabilities nc = networkCapabilitiesRestrictedForCallerPermissions(
- networkAgent.networkCapabilities, nri.mPid, nri.mUid);
- putParcelable(bundle, nc);
+ final NetworkCapabilities netCap =
+ networkCapabilitiesRestrictedForCallerPermissions(
+ networkAgent.networkCapabilities, nri.mPid, nri.mUid);
+ putParcelable(
+ bundle,
+ maybeSanitizeLocationInfoForCaller(
+ netCap, nri.mUid, nri.request.getRequestorPackageName()));
break;
}
case ConnectivityManager.CALLBACK_IP_CHANGED: {
@@ -7528,6 +7559,13 @@
*/
public int getConnectionOwnerUid(ConnectionInfo connectionInfo) {
final Vpn vpn = enforceActiveVpnOrNetworkStackPermission();
+
+ // Only VpnService based VPNs should be able to get this information.
+ if (vpn != null && vpn.getActiveAppVpnType() != VpnManager.TYPE_VPN_SERVICE) {
+ throw new SecurityException(
+ "getConnectionOwnerUid() not allowed for non-VpnService VPNs");
+ }
+
if (connectionInfo.protocol != IPPROTO_TCP && connectionInfo.protocol != IPPROTO_UDP) {
throw new IllegalArgumentException("Unsupported protocol " + connectionInfo.protocol);
}
diff --git a/services/core/java/com/android/server/DynamicSystemService.java b/services/core/java/com/android/server/DynamicSystemService.java
index c60460f..41207c9 100644
--- a/services/core/java/com/android/server/DynamicSystemService.java
+++ b/services/core/java/com/android/server/DynamicSystemService.java
@@ -18,6 +18,7 @@
import android.content.Context;
import android.content.pm.PackageManager;
+import android.gsi.AvbPublicKey;
import android.gsi.GsiProgress;
import android.gsi.IGsiService;
import android.gsi.IGsid;
@@ -227,4 +228,13 @@
throw new RuntimeException(e.toString());
}
}
+
+ @Override
+ public boolean getAvbPublicKey(AvbPublicKey dst) {
+ try {
+ return getGsiService().getAvbPublicKey(dst) == 0;
+ } catch (RemoteException e) {
+ throw new RuntimeException(e.toString());
+ }
+ }
}
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index f0a3bfd..af7af5f 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -1357,7 +1357,7 @@
public void notifyCarrierNetworkChange(boolean active) {
// only CarrierService with carrier privilege rule should have the permission
int[] subIds = Arrays.stream(SubscriptionManager.from(mContext)
- .getActiveSubscriptionIdList(false))
+ .getActiveAndHiddenSubscriptionIdList())
.filter(i -> TelephonyPermissions.checkCarrierPrivilegeForSubId(mContext,
i)).toArray();
if (ArrayUtils.isEmpty(subIds)) {
diff --git a/services/core/java/com/android/server/UserspaceRebootLogger.java b/services/core/java/com/android/server/UserspaceRebootLogger.java
new file mode 100644
index 0000000..74f113f
--- /dev/null
+++ b/services/core/java/com/android/server/UserspaceRebootLogger.java
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server;
+
+import static com.android.internal.util.FrameworkStatsLog.USERSPACE_REBOOT_REPORTED__OUTCOME__FAILED_SHUTDOWN_SEQUENCE_ABORTED;
+import static com.android.internal.util.FrameworkStatsLog.USERSPACE_REBOOT_REPORTED__OUTCOME__FAILED_USERDATA_REMOUNT;
+import static com.android.internal.util.FrameworkStatsLog.USERSPACE_REBOOT_REPORTED__OUTCOME__FAILED_USERSPACE_REBOOT_WATCHDOG_TRIGGERED;
+import static com.android.internal.util.FrameworkStatsLog.USERSPACE_REBOOT_REPORTED__OUTCOME__OUTCOME_UNKNOWN;
+import static com.android.internal.util.FrameworkStatsLog.USERSPACE_REBOOT_REPORTED__OUTCOME__SUCCESS;
+import static com.android.internal.util.FrameworkStatsLog.USERSPACE_REBOOT_REPORTED__USER_ENCRYPTION_STATE__LOCKED;
+import static com.android.internal.util.FrameworkStatsLog.USERSPACE_REBOOT_REPORTED__USER_ENCRYPTION_STATE__UNLOCKED;
+
+import android.os.SystemClock;
+import android.os.SystemProperties;
+import android.util.Slog;
+
+import com.android.internal.util.FrameworkStatsLog;
+
+import java.util.concurrent.Executor;
+
+/**
+ * Utility class to help abstract logging {@code UserspaceRebootReported} atom.
+ */
+public final class UserspaceRebootLogger {
+
+ private static final String TAG = "UserspaceRebootLogger";
+
+ private static final String USERSPACE_REBOOT_SHOULD_LOG_PROPERTY =
+ "persist.sys.userspace_reboot.log.should_log";
+ private static final String USERSPACE_REBOOT_LAST_STARTED_PROPERTY =
+ "sys.userspace_reboot.log.last_started";
+ private static final String USERSPACE_REBOOT_LAST_FINISHED_PROPERTY =
+ "sys.userspace_reboot.log.last_finished";
+ private static final String BOOT_REASON_PROPERTY = "sys.boot.reason";
+
+ private UserspaceRebootLogger() {}
+
+ /**
+ * Modifies internal state to note that {@code UserspaceRebootReported} atom needs to be
+ * logged on the next successful boot.
+ */
+ public static void noteUserspaceRebootWasRequested() {
+ SystemProperties.set(USERSPACE_REBOOT_SHOULD_LOG_PROPERTY, "1");
+ SystemProperties.set(USERSPACE_REBOOT_LAST_STARTED_PROPERTY,
+ String.valueOf(SystemClock.elapsedRealtime()));
+ }
+
+ /**
+ * Updates internal state on boot after successful userspace reboot.
+ *
+ * <p>Should be called right before framework sets {@code sys.boot_completed} property.
+ */
+ public static void noteUserspaceRebootSuccess() {
+ SystemProperties.set(USERSPACE_REBOOT_LAST_FINISHED_PROPERTY,
+ String.valueOf(SystemClock.elapsedRealtime()));
+ }
+
+ /**
+ * Returns {@code true} if {@code UserspaceRebootReported} atom should be logged.
+ */
+ public static boolean shouldLogUserspaceRebootEvent() {
+ return SystemProperties.getBoolean(USERSPACE_REBOOT_SHOULD_LOG_PROPERTY, false);
+ }
+
+ /**
+ * Asynchronously logs {@code UserspaceRebootReported} on the given {@code executor}.
+ *
+ * <p>Should be called in the end of {@link
+ * com.android.server.am.ActivityManagerService#finishBooting()} method, after framework have
+ * tried to proactivelly unlock storage of the primary user.
+ */
+ public static void logEventAsync(boolean userUnlocked, Executor executor) {
+ final int outcome = computeOutcome();
+ final long durationMillis;
+ if (outcome == USERSPACE_REBOOT_REPORTED__OUTCOME__SUCCESS) {
+ durationMillis = SystemProperties.getLong(USERSPACE_REBOOT_LAST_FINISHED_PROPERTY, 0)
+ - SystemProperties.getLong(USERSPACE_REBOOT_LAST_STARTED_PROPERTY, 0);
+ } else {
+ durationMillis = 0;
+ }
+ final int encryptionState =
+ userUnlocked
+ ? USERSPACE_REBOOT_REPORTED__USER_ENCRYPTION_STATE__UNLOCKED
+ : USERSPACE_REBOOT_REPORTED__USER_ENCRYPTION_STATE__LOCKED;
+ executor.execute(
+ () -> {
+ Slog.i(TAG, "Logging UserspaceRebootReported atom: { outcome: " + outcome
+ + " durationMillis: " + durationMillis + " encryptionState: "
+ + encryptionState + " }");
+ FrameworkStatsLog.write(FrameworkStatsLog.USERSPACE_REBOOT_REPORTED, outcome,
+ durationMillis, encryptionState);
+ SystemProperties.set(USERSPACE_REBOOT_SHOULD_LOG_PROPERTY, "");
+ });
+ }
+
+ private static int computeOutcome() {
+ if (SystemProperties.getLong(USERSPACE_REBOOT_LAST_STARTED_PROPERTY, -1) != -1) {
+ return USERSPACE_REBOOT_REPORTED__OUTCOME__SUCCESS;
+ }
+ String reason = SystemProperties.get(BOOT_REASON_PROPERTY, "");
+ if (reason.startsWith("reboot,")) {
+ reason = reason.substring("reboot".length());
+ }
+ switch (reason) {
+ case "userspace_failed,watchdog_fork":
+ // Since fork happens before shutdown sequence, attribute it to
+ // USERSPACE_REBOOT_REPORTED__OUTCOME__FAILED_SHUTDOWN_SEQUENCE_ABORTED.
+ case "userspace_failed,shutdown_aborted":
+ return USERSPACE_REBOOT_REPORTED__OUTCOME__FAILED_SHUTDOWN_SEQUENCE_ABORTED;
+ case "userspace_failed,init_user0_failed":
+ // init_user0 will fail if userdata wasn't remounted correctly, attribute to
+ // USERSPACE_REBOOT_REPORTED__OUTCOME__FAILED_USERDATA_REMOUNT.
+ case "mount_userdata_failed":
+ return USERSPACE_REBOOT_REPORTED__OUTCOME__FAILED_USERDATA_REMOUNT;
+ case "userspace_failed,watchdog_triggered":
+ return
+ USERSPACE_REBOOT_REPORTED__OUTCOME__FAILED_USERSPACE_REBOOT_WATCHDOG_TRIGGERED;
+ default:
+ return USERSPACE_REBOOT_REPORTED__OUTCOME__OUTCOME_UNKNOWN;
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/adb/AdbDebuggingManager.java b/services/core/java/com/android/server/adb/AdbDebuggingManager.java
index 4b48ef9..4a6c2be 100644
--- a/services/core/java/com/android/server/adb/AdbDebuggingManager.java
+++ b/services/core/java/com/android/server/adb/AdbDebuggingManager.java
@@ -20,18 +20,37 @@
import android.annotation.TestApi;
import android.app.ActivityManager;
+import android.app.Notification;
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
import android.content.ActivityNotFoundException;
+import android.content.BroadcastReceiver;
import android.content.ComponentName;
+import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
+import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.content.pm.UserInfo;
import android.content.res.Resources;
import android.database.ContentObserver;
+import android.debug.AdbManager;
import android.debug.AdbProtoEnums;
+import android.debug.AdbTransportType;
+import android.debug.PairDevice;
+import android.net.ConnectivityManager;
import android.net.LocalSocket;
import android.net.LocalSocketAddress;
+import android.net.NetworkInfo;
import android.net.Uri;
+import android.net.nsd.NsdManager;
+import android.net.nsd.NsdServiceInfo;
+import android.net.wifi.WifiConfiguration;
+import android.net.wifi.WifiInfo;
+import android.net.wifi.WifiManager;
+import android.net.wifi.WifiSsid;
+import android.os.Bundle;
import android.os.Environment;
import android.os.FileUtils;
import android.os.Handler;
@@ -51,6 +70,8 @@
import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
+import com.android.internal.notification.SystemNotificationChannels;
import com.android.internal.util.FastXmlSerializer;
import com.android.internal.util.XmlUtils;
import com.android.internal.util.dump.DualDumpOutputStream;
@@ -70,6 +91,8 @@
import java.io.OutputStream;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
+import java.security.SecureRandom;
+import java.util.AbstractMap;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
@@ -78,6 +101,7 @@
import java.util.List;
import java.util.Map;
import java.util.Set;
+import java.util.concurrent.atomic.AtomicBoolean;
/**
* Provides communication to the Android Debug Bridge daemon to allow, deny, or clear public keysi
@@ -86,6 +110,7 @@
public class AdbDebuggingManager {
private static final String TAG = "AdbDebuggingManager";
private static final boolean DEBUG = false;
+ private static final boolean MDNS_DEBUG = false;
private static final String ADBD_SOCKET = "adbd";
private static final String ADB_DIRECTORY = "misc/adb";
@@ -97,19 +122,39 @@
private static final int BUFFER_SIZE = 65536;
private final Context mContext;
+ private final ContentResolver mContentResolver;
private final Handler mHandler;
private AdbDebuggingThread mThread;
- private boolean mAdbEnabled = false;
+ private boolean mAdbUsbEnabled = false;
+ private boolean mAdbWifiEnabled = false;
private String mFingerprints;
- private final List<String> mConnectedKeys;
+ // A key can be used more than once (e.g. USB, wifi), so need to keep a refcount
+ private final Map<String, Integer> mConnectedKeys;
private String mConfirmComponent;
private final File mTestUserKeyFile;
+ private static final String WIFI_PERSISTENT_CONFIG_PROPERTY =
+ "persist.adb.tls_server.enable";
+ private static final String WIFI_PERSISTENT_GUID =
+ "persist.adb.wifi.guid";
+ private static final int PAIRING_CODE_LENGTH = 6;
+ private PairingThread mPairingThread = null;
+ // A list of keys connected via wifi
+ private final Set<String> mWifiConnectedKeys;
+ // The current info of the adbwifi connection.
+ private AdbConnectionInfo mAdbConnectionInfo;
+ // Polls for a tls port property when adb wifi is enabled
+ private AdbConnectionPortPoller mConnectionPortPoller;
+ private final PortListenerImpl mPortListener = new PortListenerImpl();
+
public AdbDebuggingManager(Context context) {
mHandler = new AdbDebuggingHandler(FgThread.get().getLooper());
mContext = context;
+ mContentResolver = mContext.getContentResolver();
mTestUserKeyFile = null;
- mConnectedKeys = new ArrayList<>(1);
+ mConnectedKeys = new HashMap<String, Integer>();
+ mWifiConnectedKeys = new HashSet<String>();
+ mAdbConnectionInfo = new AdbConnectionInfo();
}
/**
@@ -120,9 +165,178 @@
protected AdbDebuggingManager(Context context, String confirmComponent, File testUserKeyFile) {
mHandler = new AdbDebuggingHandler(FgThread.get().getLooper());
mContext = context;
+ mContentResolver = mContext.getContentResolver();
mConfirmComponent = confirmComponent;
mTestUserKeyFile = testUserKeyFile;
- mConnectedKeys = new ArrayList<>();
+ mConnectedKeys = new HashMap<String, Integer>();
+ mWifiConnectedKeys = new HashSet<String>();
+ mAdbConnectionInfo = new AdbConnectionInfo();
+ }
+
+ class PairingThread extends Thread implements NsdManager.RegistrationListener {
+ private NsdManager mNsdManager;
+ private String mPublicKey;
+ private String mPairingCode;
+ private String mGuid;
+ private String mServiceName;
+ private final String mServiceType = "_adb_secure_pairing._tcp.";
+ private int mPort;
+
+ private native int native_pairing_start(String guid, String password);
+ private native void native_pairing_cancel();
+ private native boolean native_pairing_wait();
+
+ PairingThread(String pairingCode, String serviceName) {
+ super(TAG);
+ mPairingCode = pairingCode;
+ mGuid = SystemProperties.get(WIFI_PERSISTENT_GUID);
+ mServiceName = serviceName;
+ if (serviceName == null || serviceName.isEmpty()) {
+ mServiceName = mGuid;
+ }
+ mPort = -1;
+ mNsdManager = (NsdManager) mContext.getSystemService(Context.NSD_SERVICE);
+ }
+
+ @Override
+ public void run() {
+ if (mGuid.isEmpty()) {
+ Slog.e(TAG, "adbwifi guid was not set");
+ return;
+ }
+ mPort = native_pairing_start(mGuid, mPairingCode);
+ if (mPort <= 0 || mPort > 65535) {
+ Slog.e(TAG, "Unable to start pairing server");
+ return;
+ }
+
+ // Register the mdns service
+ NsdServiceInfo serviceInfo = new NsdServiceInfo();
+ serviceInfo.setServiceName(mServiceName);
+ serviceInfo.setServiceType(mServiceType);
+ serviceInfo.setPort(mPort);
+ mNsdManager.registerService(serviceInfo, NsdManager.PROTOCOL_DNS_SD, this);
+
+ // Send pairing port to UI
+ Message msg = mHandler.obtainMessage(
+ AdbDebuggingHandler.MSG_RESPONSE_PAIRING_PORT);
+ msg.obj = mPort;
+ mHandler.sendMessage(msg);
+
+ boolean paired = native_pairing_wait();
+ if (DEBUG) {
+ if (mPublicKey != null) {
+ Slog.i(TAG, "Pairing succeeded key=" + mPublicKey);
+ } else {
+ Slog.i(TAG, "Pairing failed");
+ }
+ }
+
+ mNsdManager.unregisterService(this);
+
+ Bundle bundle = new Bundle();
+ bundle.putString("publicKey", paired ? mPublicKey : null);
+ Message message = Message.obtain(mHandler,
+ AdbDebuggingHandler.MSG_RESPONSE_PAIRING_RESULT,
+ bundle);
+ mHandler.sendMessage(message);
+ }
+
+ public void cancelPairing() {
+ native_pairing_cancel();
+ }
+
+ @Override
+ public void onServiceRegistered(NsdServiceInfo serviceInfo) {
+ if (MDNS_DEBUG) Slog.i(TAG, "Registered pairing service: " + serviceInfo);
+ }
+
+ @Override
+ public void onRegistrationFailed(NsdServiceInfo serviceInfo, int errorCode) {
+ Slog.e(TAG, "Failed to register pairing service(err=" + errorCode
+ + "): " + serviceInfo);
+ cancelPairing();
+ }
+
+ @Override
+ public void onServiceUnregistered(NsdServiceInfo serviceInfo) {
+ if (MDNS_DEBUG) Slog.i(TAG, "Unregistered pairing service: " + serviceInfo);
+ }
+
+ @Override
+ public void onUnregistrationFailed(NsdServiceInfo serviceInfo, int errorCode) {
+ Slog.w(TAG, "Failed to unregister pairing service(err=" + errorCode
+ + "): " + serviceInfo);
+ }
+ }
+
+ interface AdbConnectionPortListener {
+ void onPortReceived(int port);
+ }
+
+ /**
+ * This class will poll for a period of time for adbd to write the port
+ * it connected to.
+ *
+ * TODO(joshuaduong): The port is being sent via system property because the adbd socket
+ * (AdbDebuggingManager) is not created when ro.adb.secure=0. Thus, we must communicate the
+ * port through different means. A better fix would be to always start AdbDebuggingManager, but
+ * it needs to adjust accordingly on whether ro.adb.secure is set.
+ */
+ static class AdbConnectionPortPoller extends Thread {
+ private final String mAdbPortProp = "service.adb.tls.port";
+ private AdbConnectionPortListener mListener;
+ private final int mDurationSecs = 10;
+ private AtomicBoolean mCanceled = new AtomicBoolean(false);
+
+ AdbConnectionPortPoller(AdbConnectionPortListener listener) {
+ mListener = listener;
+ }
+
+ @Override
+ public void run() {
+ if (DEBUG) Slog.d(TAG, "Starting adb port property poller");
+ // Once adbwifi is enabled, we poll the service.adb.tls.port
+ // system property until we get the port, or -1 on failure.
+ // Let's also limit the polling to 10 seconds, just in case
+ // something went wrong.
+ for (int i = 0; i < mDurationSecs; ++i) {
+ if (mCanceled.get()) {
+ return;
+ }
+
+ // If the property is set to -1, then that means adbd has failed
+ // to start the server. Otherwise we should have a valid port.
+ int port = SystemProperties.getInt(mAdbPortProp, Integer.MAX_VALUE);
+ if (port == -1 || (port > 0 && port <= 65535)) {
+ mListener.onPortReceived(port);
+ return;
+ }
+ SystemClock.sleep(1000);
+ }
+ Slog.w(TAG, "Failed to receive adb connection port");
+ mListener.onPortReceived(-1);
+ }
+
+ public void cancelAndWait() {
+ mCanceled.set(true);
+ if (this.isAlive()) {
+ try {
+ this.join();
+ } catch (InterruptedException e) {
+ }
+ }
+ }
+ }
+
+ class PortListenerImpl implements AdbConnectionPortListener {
+ public void onPortReceived(int port) {
+ Message msg = mHandler.obtainMessage(port > 0
+ ? AdbDebuggingHandler.MSG_SERVER_CONNECTED
+ : AdbDebuggingHandler.MSG_SERVER_DISCONNECTED);
+ msg.obj = port;
+ mHandler.sendMessage(msg);
+ }
}
class AdbDebuggingThread extends Thread {
@@ -212,6 +426,46 @@
AdbDebuggingHandler.MESSAGE_ADB_CONNECTED_KEY);
msg.obj = key;
mHandler.sendMessage(msg);
+ } else if (buffer[0] == 'W' && buffer[1] == 'E') {
+ // adbd_auth.h and AdbTransportType.aidl need to be kept in
+ // sync.
+ byte transportType = buffer[2];
+ String key = new String(Arrays.copyOfRange(buffer, 3, count));
+ if (transportType == AdbTransportType.USB) {
+ Slog.d(TAG, "Received USB TLS connected key message: " + key);
+ Message msg = mHandler.obtainMessage(
+ AdbDebuggingHandler.MESSAGE_ADB_CONNECTED_KEY);
+ msg.obj = key;
+ mHandler.sendMessage(msg);
+ } else if (transportType == AdbTransportType.WIFI) {
+ Slog.d(TAG, "Received WIFI TLS connected key message: " + key);
+ Message msg = mHandler.obtainMessage(
+ AdbDebuggingHandler.MSG_WIFI_DEVICE_CONNECTED);
+ msg.obj = key;
+ mHandler.sendMessage(msg);
+ } else {
+ Slog.e(TAG, "Got unknown transport type from adbd (" + transportType
+ + ")");
+ }
+ } else if (buffer[0] == 'W' && buffer[1] == 'F') {
+ byte transportType = buffer[2];
+ String key = new String(Arrays.copyOfRange(buffer, 3, count));
+ if (transportType == AdbTransportType.USB) {
+ Slog.d(TAG, "Received USB TLS disconnect message: " + key);
+ Message msg = mHandler.obtainMessage(
+ AdbDebuggingHandler.MESSAGE_ADB_DISCONNECT);
+ msg.obj = key;
+ mHandler.sendMessage(msg);
+ } else if (transportType == AdbTransportType.WIFI) {
+ Slog.d(TAG, "Received WIFI TLS disconnect key message: " + key);
+ Message msg = mHandler.obtainMessage(
+ AdbDebuggingHandler.MSG_WIFI_DEVICE_DISCONNECTED);
+ msg.obj = key;
+ mHandler.sendMessage(msg);
+ } else {
+ Slog.e(TAG, "Got unknown transport type from adbd (" + transportType
+ + ")");
+ }
} else {
Slog.e(TAG, "Wrong message: "
+ (new String(Arrays.copyOfRange(buffer, 0, 2))));
@@ -267,7 +521,156 @@
}
}
+ class AdbConnectionInfo {
+ private String mBssid;
+ private String mSsid;
+ private int mPort;
+
+ AdbConnectionInfo() {
+ mBssid = "";
+ mSsid = "";
+ mPort = -1;
+ }
+
+ AdbConnectionInfo(String bssid, String ssid) {
+ mBssid = bssid;
+ mSsid = ssid;
+ }
+
+ AdbConnectionInfo(AdbConnectionInfo other) {
+ mBssid = other.mBssid;
+ mSsid = other.mSsid;
+ mPort = other.mPort;
+ }
+
+ public String getBSSID() {
+ return mBssid;
+ }
+
+ public String getSSID() {
+ return mSsid;
+ }
+
+ public int getPort() {
+ return mPort;
+ }
+
+ public void setPort(int port) {
+ mPort = port;
+ }
+
+ public void clear() {
+ mBssid = "";
+ mSsid = "";
+ mPort = -1;
+ }
+ }
+
+ private void setAdbConnectionInfo(AdbConnectionInfo info) {
+ synchronized (mAdbConnectionInfo) {
+ if (info == null) {
+ mAdbConnectionInfo.clear();
+ return;
+ }
+ mAdbConnectionInfo = info;
+ }
+ }
+
+ private AdbConnectionInfo getAdbConnectionInfo() {
+ synchronized (mAdbConnectionInfo) {
+ return new AdbConnectionInfo(mAdbConnectionInfo);
+ }
+ }
+
class AdbDebuggingHandler extends Handler {
+ private NotificationManager mNotificationManager;
+ private boolean mAdbNotificationShown;
+
+ private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ String action = intent.getAction();
+ // We only care about when wifi is disabled, and when there is a wifi network
+ // change.
+ if (WifiManager.WIFI_STATE_CHANGED_ACTION.equals(action)) {
+ int state = intent.getIntExtra(
+ WifiManager.EXTRA_WIFI_STATE, WifiManager.WIFI_STATE_DISABLED);
+ if (state == WifiManager.WIFI_STATE_DISABLED) {
+ Slog.i(TAG, "Wifi disabled. Disabling adbwifi.");
+ Settings.Global.putInt(mContentResolver,
+ Settings.Global.ADB_WIFI_ENABLED, 0);
+ }
+ } else if (WifiManager.NETWORK_STATE_CHANGED_ACTION.equals(action)) {
+ // We only care about wifi type connections
+ NetworkInfo networkInfo = (NetworkInfo) intent.getParcelableExtra(
+ WifiManager.EXTRA_NETWORK_INFO);
+ if (networkInfo.getType() == ConnectivityManager.TYPE_WIFI) {
+ // Check for network disconnect
+ if (!networkInfo.isConnected()) {
+ Slog.i(TAG, "Network disconnected. Disabling adbwifi.");
+ Settings.Global.putInt(mContentResolver,
+ Settings.Global.ADB_WIFI_ENABLED, 0);
+ return;
+ }
+
+ WifiManager wifiManager =
+ (WifiManager) mContext.getSystemService(Context.WIFI_SERVICE);
+ WifiInfo wifiInfo = wifiManager.getConnectionInfo();
+ if (wifiInfo == null || wifiInfo.getNetworkId() == -1) {
+ Slog.i(TAG, "Not connected to any wireless network."
+ + " Not enabling adbwifi.");
+ Settings.Global.putInt(mContentResolver,
+ Settings.Global.ADB_WIFI_ENABLED, 0);
+ }
+
+ // Check for network change
+ String bssid = wifiInfo.getBSSID();
+ if (bssid == null || bssid.isEmpty()) {
+ Slog.e(TAG, "Unable to get the wifi ap's BSSID. Disabling adbwifi.");
+ Settings.Global.putInt(mContentResolver,
+ Settings.Global.ADB_WIFI_ENABLED, 0);
+ }
+ synchronized (mAdbConnectionInfo) {
+ if (!bssid.equals(mAdbConnectionInfo.getBSSID())) {
+ Slog.i(TAG, "Detected wifi network change. Disabling adbwifi.");
+ Settings.Global.putInt(mContentResolver,
+ Settings.Global.ADB_WIFI_ENABLED, 0);
+ }
+ }
+ }
+ }
+ }
+ };
+
+ private static final String ADB_NOTIFICATION_CHANNEL_ID_TV = "usbdevicemanager.adb.tv";
+
+ private boolean isTv() {
+ return mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_LEANBACK);
+ }
+
+ private void setupNotifications() {
+ if (mNotificationManager != null) {
+ return;
+ }
+ mNotificationManager = (NotificationManager)
+ mContext.getSystemService(Context.NOTIFICATION_SERVICE);
+ if (mNotificationManager == null) {
+ Slog.e(TAG, "Unable to setup notifications for wireless debugging");
+ return;
+ }
+
+ // Ensure that the notification channels are set up
+ if (isTv()) {
+ // TV-specific notification channel
+ mNotificationManager.createNotificationChannel(
+ new NotificationChannel(ADB_NOTIFICATION_CHANNEL_ID_TV,
+ mContext.getString(
+ com.android.internal.R.string
+ .adb_debugging_notification_channel_tv),
+ NotificationManager.IMPORTANCE_HIGH));
+ }
+ }
+
// The default time to schedule the job to keep the keystore updated with a currently
// connected key as well as to removed expired keys.
static final long UPDATE_KEYSTORE_JOB_INTERVAL = 86400000;
@@ -287,8 +690,50 @@
static final int MESSAGE_ADB_UPDATE_KEYSTORE = 9;
static final int MESSAGE_ADB_CONNECTED_KEY = 10;
+ // === Messages from the UI ==============
+ // UI asks adbd to enable adbdwifi
+ static final int MSG_ADBDWIFI_ENABLE = 11;
+ // UI asks adbd to disable adbdwifi
+ static final int MSG_ADBDWIFI_DISABLE = 12;
+ // Cancel pairing
+ static final int MSG_PAIRING_CANCEL = 14;
+ // Enable pairing by pairing code
+ static final int MSG_PAIR_PAIRING_CODE = 15;
+ // Enable pairing by QR code
+ static final int MSG_PAIR_QR_CODE = 16;
+ // UI asks to unpair (forget) a device.
+ static final int MSG_REQ_UNPAIR = 17;
+ // User allows debugging on the current network
+ static final int MSG_ADBWIFI_ALLOW = 18;
+ // User denies debugging on the current network
+ static final int MSG_ADBWIFI_DENY = 19;
+
+ // === Messages from the PairingThread ===========
+ // Result of the pairing
+ static final int MSG_RESPONSE_PAIRING_RESULT = 20;
+ // The port opened for pairing
+ static final int MSG_RESPONSE_PAIRING_PORT = 21;
+
+ // === Messages from adbd ================
+ // Notifies us a wifi device connected.
+ static final int MSG_WIFI_DEVICE_CONNECTED = 22;
+ // Notifies us a wifi device disconnected.
+ static final int MSG_WIFI_DEVICE_DISCONNECTED = 23;
+ // Notifies us the TLS server is connected and listening
+ static final int MSG_SERVER_CONNECTED = 24;
+ // Notifies us the TLS server is disconnected
+ static final int MSG_SERVER_DISCONNECTED = 25;
+
+ // === Messages we can send to adbd ===========
+ static final String MSG_DISCONNECT_DEVICE = "DD";
+ static final String MSG_DISABLE_ADBDWIFI = "DA";
+
private AdbKeyStore mAdbKeyStore;
+ // Usb, Wi-Fi transports can be enabled together or separately, so don't break the framework
+ // connection unless all transport types are disconnected.
+ private int mAdbEnabledRefCount = 0;
+
private ContentObserver mAuthTimeObserver = new ContentObserver(this) {
@Override
public void onChange(boolean selfChange, Uri uri) {
@@ -313,44 +758,111 @@
mAdbKeyStore = adbKeyStore;
}
+ // Show when at least one device is connected.
+ public void showAdbConnectedNotification(boolean show) {
+ final int id = SystemMessage.NOTE_ADB_WIFI_ACTIVE;
+ final int titleRes = com.android.internal.R.string.adbwifi_active_notification_title;
+ if (show == mAdbNotificationShown) {
+ return;
+ }
+ setupNotifications();
+ if (!mAdbNotificationShown) {
+ Resources r = mContext.getResources();
+ CharSequence title = r.getText(titleRes);
+ CharSequence message = r.getText(
+ com.android.internal.R.string.adbwifi_active_notification_message);
+
+ Intent intent = new Intent(Settings.ACTION_APPLICATION_DEVELOPMENT_SETTINGS);
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK
+ | Intent.FLAG_ACTIVITY_CLEAR_TASK);
+ PendingIntent pi = PendingIntent.getActivityAsUser(mContext, 0,
+ intent, 0, null, UserHandle.CURRENT);
+
+ Notification notification =
+ new Notification.Builder(mContext, SystemNotificationChannels.DEVELOPER)
+ .setSmallIcon(com.android.internal.R.drawable.stat_sys_adb)
+ .setWhen(0)
+ .setOngoing(true)
+ .setTicker(title)
+ .setDefaults(0) // please be quiet
+ .setColor(mContext.getColor(
+ com.android.internal.R.color
+ .system_notification_accent_color))
+ .setContentTitle(title)
+ .setContentText(message)
+ .setContentIntent(pi)
+ .setVisibility(Notification.VISIBILITY_PUBLIC)
+ .extend(new Notification.TvExtender()
+ .setChannelId(ADB_NOTIFICATION_CHANNEL_ID_TV))
+ .build();
+ mAdbNotificationShown = true;
+ mNotificationManager.notifyAsUser(null, id, notification,
+ UserHandle.ALL);
+ } else {
+ mAdbNotificationShown = false;
+ mNotificationManager.cancelAsUser(null, id, UserHandle.ALL);
+ }
+ }
+
+ private void startAdbDebuggingThread() {
+ ++mAdbEnabledRefCount;
+ if (DEBUG) Slog.i(TAG, "startAdbDebuggingThread ref=" + mAdbEnabledRefCount);
+ if (mAdbEnabledRefCount > 1) {
+ return;
+ }
+
+ registerForAuthTimeChanges();
+ mThread = new AdbDebuggingThread();
+ mThread.start();
+
+ mAdbKeyStore.updateKeyStore();
+ scheduleJobToUpdateAdbKeyStore();
+ }
+
+ private void stopAdbDebuggingThread() {
+ --mAdbEnabledRefCount;
+ if (DEBUG) Slog.i(TAG, "stopAdbDebuggingThread ref=" + mAdbEnabledRefCount);
+ if (mAdbEnabledRefCount > 0) {
+ return;
+ }
+
+ if (mThread != null) {
+ mThread.stopListening();
+ mThread = null;
+ }
+
+ if (!mConnectedKeys.isEmpty()) {
+ for (Map.Entry<String, Integer> entry : mConnectedKeys.entrySet()) {
+ mAdbKeyStore.setLastConnectionTime(entry.getKey(),
+ System.currentTimeMillis());
+ }
+ sendPersistKeyStoreMessage();
+ mConnectedKeys.clear();
+ mWifiConnectedKeys.clear();
+ }
+ scheduleJobToUpdateAdbKeyStore();
+ }
+
public void handleMessage(Message msg) {
+ if (mAdbKeyStore == null) {
+ mAdbKeyStore = new AdbKeyStore();
+ }
+
switch (msg.what) {
case MESSAGE_ADB_ENABLED:
- if (mAdbEnabled) {
+ if (mAdbUsbEnabled) {
break;
}
- registerForAuthTimeChanges();
- mAdbEnabled = true;
-
- mThread = new AdbDebuggingThread();
- mThread.start();
-
- mAdbKeyStore = new AdbKeyStore();
- mAdbKeyStore.updateKeyStore();
- scheduleJobToUpdateAdbKeyStore();
+ startAdbDebuggingThread();
+ mAdbUsbEnabled = true;
break;
case MESSAGE_ADB_DISABLED:
- if (!mAdbEnabled) {
+ if (!mAdbUsbEnabled) {
break;
}
-
- mAdbEnabled = false;
-
- if (mThread != null) {
- mThread.stopListening();
- mThread = null;
- }
-
- if (!mConnectedKeys.isEmpty()) {
- for (String connectedKey : mConnectedKeys) {
- mAdbKeyStore.setLastConnectionTime(connectedKey,
- System.currentTimeMillis());
- }
- sendPersistKeyStoreMessage();
- mConnectedKeys.clear();
- }
- scheduleJobToUpdateAdbKeyStore();
+ stopAdbDebuggingThread();
+ mAdbUsbEnabled = false;
break;
case MESSAGE_ADB_ALLOW: {
@@ -366,8 +878,8 @@
if (mThread != null) {
mThread.sendResponse("OK");
if (alwaysAllow) {
- if (!mConnectedKeys.contains(key)) {
- mConnectedKeys.add(key);
+ if (!mConnectedKeys.containsKey(key)) {
+ mConnectedKeys.put(key, 1);
}
mAdbKeyStore.setLastConnectionTime(key, System.currentTimeMillis());
sendPersistKeyStoreMessage();
@@ -406,13 +918,14 @@
}
logAdbConnectionChanged(key, AdbProtoEnums.AWAITING_USER_APPROVAL, false);
mFingerprints = fingerprints;
- startConfirmation(key, mFingerprints);
+ startConfirmationForKey(key, mFingerprints);
break;
}
case MESSAGE_ADB_CLEAR: {
Slog.d(TAG, "Received a request to clear the adb authorizations");
mConnectedKeys.clear();
+ mWifiConnectedKeys.clear();
mAdbKeyStore.deleteKeyStore();
cancelJobToUpdateAdbKeyStore();
break;
@@ -422,12 +935,17 @@
String key = (String) msg.obj;
boolean alwaysAllow = false;
if (key != null && key.length() > 0) {
- if (mConnectedKeys.contains(key)) {
+ if (mConnectedKeys.containsKey(key)) {
alwaysAllow = true;
- mAdbKeyStore.setLastConnectionTime(key, System.currentTimeMillis());
- sendPersistKeyStoreMessage();
- scheduleJobToUpdateAdbKeyStore();
- mConnectedKeys.remove(key);
+ int refcount = mConnectedKeys.get(key) - 1;
+ if (refcount == 0) {
+ mAdbKeyStore.setLastConnectionTime(key, System.currentTimeMillis());
+ sendPersistKeyStoreMessage();
+ scheduleJobToUpdateAdbKeyStore();
+ mConnectedKeys.remove(key);
+ } else {
+ mConnectedKeys.put(key, refcount);
+ }
}
} else {
Slog.w(TAG, "Received a disconnected key message with an empty key");
@@ -445,8 +963,8 @@
case MESSAGE_ADB_UPDATE_KEYSTORE: {
if (!mConnectedKeys.isEmpty()) {
- for (String connectedKey : mConnectedKeys) {
- mAdbKeyStore.setLastConnectionTime(connectedKey,
+ for (Map.Entry<String, Integer> entry : mConnectedKeys.entrySet()) {
+ mAdbKeyStore.setLastConnectionTime(entry.getKey(),
System.currentTimeMillis());
}
sendPersistKeyStoreMessage();
@@ -463,8 +981,10 @@
if (key == null || key.length() == 0) {
Slog.w(TAG, "Received a connected key message with an empty key");
} else {
- if (!mConnectedKeys.contains(key)) {
- mConnectedKeys.add(key);
+ if (!mConnectedKeys.containsKey(key)) {
+ mConnectedKeys.put(key, 1);
+ } else {
+ mConnectedKeys.put(key, mConnectedKeys.get(key) + 1);
}
mAdbKeyStore.setLastConnectionTime(key, System.currentTimeMillis());
sendPersistKeyStoreMessage();
@@ -473,6 +993,199 @@
}
break;
}
+ case MSG_ADBDWIFI_ENABLE: {
+ if (mAdbWifiEnabled) {
+ break;
+ }
+
+ AdbConnectionInfo currentInfo = getCurrentWifiApInfo();
+ if (currentInfo == null) {
+ Settings.Global.putInt(mContentResolver,
+ Settings.Global.ADB_WIFI_ENABLED, 0);
+ break;
+ }
+
+ if (!verifyWifiNetwork(currentInfo.getBSSID(),
+ currentInfo.getSSID())) {
+ // This means that the network is not in the list of trusted networks.
+ // We'll give user a prompt on whether to allow wireless debugging on
+ // the current wifi network.
+ Settings.Global.putInt(mContentResolver,
+ Settings.Global.ADB_WIFI_ENABLED, 0);
+ break;
+ }
+
+ setAdbConnectionInfo(currentInfo);
+ IntentFilter intentFilter =
+ new IntentFilter(WifiManager.WIFI_STATE_CHANGED_ACTION);
+ intentFilter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
+ mContext.registerReceiver(mBroadcastReceiver, intentFilter);
+
+ SystemProperties.set(WIFI_PERSISTENT_CONFIG_PROPERTY, "1");
+ mConnectionPortPoller =
+ new AdbDebuggingManager.AdbConnectionPortPoller(mPortListener);
+ mConnectionPortPoller.start();
+
+ startAdbDebuggingThread();
+ mAdbWifiEnabled = true;
+
+ if (DEBUG) Slog.i(TAG, "adb start wireless adb");
+ break;
+ }
+ case MSG_ADBDWIFI_DISABLE:
+ if (!mAdbWifiEnabled) {
+ break;
+ }
+ mAdbWifiEnabled = false;
+ setAdbConnectionInfo(null);
+ mContext.unregisterReceiver(mBroadcastReceiver);
+
+ if (mThread != null) {
+ mThread.sendResponse(MSG_DISABLE_ADBDWIFI);
+ }
+ onAdbdWifiServerDisconnected(-1);
+ stopAdbDebuggingThread();
+ break;
+ case MSG_ADBWIFI_ALLOW:
+ if (mAdbWifiEnabled) {
+ break;
+ }
+ String bssid = (String) msg.obj;
+ boolean alwaysAllow = msg.arg1 == 1;
+ if (alwaysAllow) {
+ mAdbKeyStore.addTrustedNetwork(bssid);
+ }
+
+ // Let's check again to make sure we didn't switch networks while verifying
+ // the wifi bssid.
+ AdbConnectionInfo newInfo = getCurrentWifiApInfo();
+ if (newInfo == null || !bssid.equals(newInfo.getBSSID())) {
+ break;
+ }
+
+ setAdbConnectionInfo(newInfo);
+ Settings.Global.putInt(mContentResolver,
+ Settings.Global.ADB_WIFI_ENABLED, 1);
+ IntentFilter intentFilter =
+ new IntentFilter(WifiManager.WIFI_STATE_CHANGED_ACTION);
+ intentFilter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
+ mContext.registerReceiver(mBroadcastReceiver, intentFilter);
+
+ SystemProperties.set(WIFI_PERSISTENT_CONFIG_PROPERTY, "1");
+ mConnectionPortPoller =
+ new AdbDebuggingManager.AdbConnectionPortPoller(mPortListener);
+ mConnectionPortPoller.start();
+
+ startAdbDebuggingThread();
+ mAdbWifiEnabled = true;
+
+ if (DEBUG) Slog.i(TAG, "adb start wireless adb");
+ break;
+ case MSG_ADBWIFI_DENY:
+ Settings.Global.putInt(mContentResolver,
+ Settings.Global.ADB_WIFI_ENABLED, 0);
+ sendServerConnectionState(false, -1);
+ break;
+ case MSG_REQ_UNPAIR: {
+ String fingerprint = (String) msg.obj;
+ // Tell adbd to disconnect the device if connected.
+ String publicKey = mAdbKeyStore.findKeyFromFingerprint(fingerprint);
+ if (publicKey == null || publicKey.isEmpty()) {
+ Slog.e(TAG, "Not a known fingerprint [" + fingerprint + "]");
+ break;
+ }
+ String cmdStr = MSG_DISCONNECT_DEVICE + publicKey;
+ if (mThread != null) {
+ mThread.sendResponse(cmdStr);
+ }
+ mAdbKeyStore.removeKey(publicKey);
+ // Send the updated paired devices list to the UI.
+ sendPairedDevicesToUI(mAdbKeyStore.getPairedDevices());
+ break;
+ }
+ case MSG_RESPONSE_PAIRING_RESULT: {
+ Bundle bundle = (Bundle) msg.obj;
+ String publicKey = bundle.getString("publicKey");
+ onPairingResult(publicKey);
+ // Send the updated paired devices list to the UI.
+ sendPairedDevicesToUI(mAdbKeyStore.getPairedDevices());
+ break;
+ }
+ case MSG_RESPONSE_PAIRING_PORT: {
+ int port = (int) msg.obj;
+ sendPairingPortToUI(port);
+ break;
+ }
+ case MSG_PAIR_PAIRING_CODE: {
+ String pairingCode = createPairingCode(PAIRING_CODE_LENGTH);
+ updateUIPairCode(pairingCode);
+ mPairingThread = new PairingThread(pairingCode, null);
+ mPairingThread.start();
+ break;
+ }
+ case MSG_PAIR_QR_CODE: {
+ Bundle bundle = (Bundle) msg.obj;
+ String serviceName = bundle.getString("serviceName");
+ String password = bundle.getString("password");
+ mPairingThread = new PairingThread(password, serviceName);
+ mPairingThread.start();
+ break;
+ }
+ case MSG_PAIRING_CANCEL:
+ if (mPairingThread != null) {
+ mPairingThread.cancelPairing();
+ try {
+ mPairingThread.join();
+ } catch (InterruptedException e) {
+ Slog.w(TAG, "Error while waiting for pairing thread to quit.");
+ e.printStackTrace();
+ }
+ mPairingThread = null;
+ }
+ break;
+ case MSG_WIFI_DEVICE_CONNECTED: {
+ String key = (String) msg.obj;
+ if (mWifiConnectedKeys.add(key)) {
+ sendPairedDevicesToUI(mAdbKeyStore.getPairedDevices());
+ showAdbConnectedNotification(true);
+ }
+ break;
+ }
+ case MSG_WIFI_DEVICE_DISCONNECTED: {
+ String key = (String) msg.obj;
+ if (mWifiConnectedKeys.remove(key)) {
+ sendPairedDevicesToUI(mAdbKeyStore.getPairedDevices());
+ if (mWifiConnectedKeys.isEmpty()) {
+ showAdbConnectedNotification(false);
+ }
+ }
+ break;
+ }
+ case MSG_SERVER_CONNECTED: {
+ int port = (int) msg.obj;
+ onAdbdWifiServerConnected(port);
+ synchronized (mAdbConnectionInfo) {
+ mAdbConnectionInfo.setPort(port);
+ }
+ Settings.Global.putInt(mContentResolver,
+ Settings.Global.ADB_WIFI_ENABLED, 1);
+ break;
+ }
+ case MSG_SERVER_DISCONNECTED: {
+ if (!mAdbWifiEnabled) {
+ break;
+ }
+ int port = (int) msg.obj;
+ onAdbdWifiServerDisconnected(port);
+ Settings.Global.putInt(mContentResolver,
+ Settings.Global.ADB_WIFI_ENABLED, 0);
+ stopAdbDebuggingThread();
+ if (mConnectionPortPoller != null) {
+ mConnectionPortPoller.cancelAndWait();
+ mConnectionPortPoller = null;
+ }
+ break;
+ }
}
}
@@ -534,6 +1247,142 @@
private void cancelJobToUpdateAdbKeyStore() {
removeMessages(AdbDebuggingHandler.MESSAGE_ADB_UPDATE_KEYSTORE);
}
+
+ // Generates a random string of digits with size |size|.
+ private String createPairingCode(int size) {
+ String res = "";
+ SecureRandom rand = new SecureRandom();
+ for (int i = 0; i < size; ++i) {
+ res += rand.nextInt(10);
+ }
+
+ return res;
+ }
+
+ private void sendServerConnectionState(boolean connected, int port) {
+ Intent intent = new Intent(AdbManager.WIRELESS_DEBUG_STATE_CHANGED_ACTION);
+ intent.putExtra(AdbManager.WIRELESS_STATUS_EXTRA, connected
+ ? AdbManager.WIRELESS_STATUS_CONNECTED
+ : AdbManager.WIRELESS_STATUS_DISCONNECTED);
+ intent.putExtra(AdbManager.WIRELESS_DEBUG_PORT_EXTRA, port);
+ mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
+ }
+
+ private void onAdbdWifiServerConnected(int port) {
+ // Send the paired devices list to the UI
+ sendPairedDevicesToUI(mAdbKeyStore.getPairedDevices());
+ sendServerConnectionState(true, port);
+ }
+
+ private void onAdbdWifiServerDisconnected(int port) {
+ // The TLS server disconnected while we had wireless debugging enabled.
+ // Let's disable it.
+ mWifiConnectedKeys.clear();
+ showAdbConnectedNotification(false);
+ sendServerConnectionState(false, port);
+ }
+
+ /**
+ * Returns the [bssid, ssid] of the current access point.
+ */
+ private AdbConnectionInfo getCurrentWifiApInfo() {
+ WifiManager wifiManager = (WifiManager) mContext.getSystemService(Context.WIFI_SERVICE);
+ WifiInfo wifiInfo = wifiManager.getConnectionInfo();
+ if (wifiInfo == null || wifiInfo.getNetworkId() == -1) {
+ Slog.i(TAG, "Not connected to any wireless network. Not enabling adbwifi.");
+ return null;
+ }
+
+ String ssid = null;
+ if (wifiInfo.isPasspointAp() || wifiInfo.isOsuAp()) {
+ ssid = wifiInfo.getPasspointProviderFriendlyName();
+ } else {
+ ssid = wifiInfo.getSSID();
+ if (ssid == null || WifiSsid.NONE.equals(ssid)) {
+ // OK, it's not in the connectionInfo; we have to go hunting for it
+ List<WifiConfiguration> networks = wifiManager.getConfiguredNetworks();
+ int length = networks.size();
+ for (int i = 0; i < length; i++) {
+ if (networks.get(i).networkId == wifiInfo.getNetworkId()) {
+ ssid = networks.get(i).SSID;
+ }
+ }
+ if (ssid == null) {
+ Slog.e(TAG, "Unable to get ssid of the wifi AP.");
+ return null;
+ }
+ }
+ }
+
+ String bssid = wifiInfo.getBSSID();
+ if (bssid == null || bssid.isEmpty()) {
+ Slog.e(TAG, "Unable to get the wifi ap's BSSID.");
+ return null;
+ }
+ return new AdbConnectionInfo(bssid, ssid);
+ }
+
+ private boolean verifyWifiNetwork(String bssid, String ssid) {
+ // Check against a list of user-trusted networks.
+ if (mAdbKeyStore.isTrustedNetwork(bssid)) {
+ return true;
+ }
+
+ // Ask user to confirm using wireless debugging on this network.
+ startConfirmationForNetwork(ssid, bssid);
+ return false;
+ }
+
+ private void onPairingResult(String publicKey) {
+ if (publicKey == null) {
+ Intent intent = new Intent(AdbManager.WIRELESS_DEBUG_PAIRING_RESULT_ACTION);
+ intent.putExtra(AdbManager.WIRELESS_STATUS_EXTRA, AdbManager.WIRELESS_STATUS_FAIL);
+ mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
+ } else {
+ Intent intent = new Intent(AdbManager.WIRELESS_DEBUG_PAIRING_RESULT_ACTION);
+ intent.putExtra(AdbManager.WIRELESS_STATUS_EXTRA,
+ AdbManager.WIRELESS_STATUS_SUCCESS);
+ String fingerprints = getFingerprints(publicKey);
+ String hostname = "nouser@nohostname";
+ String[] args = publicKey.split("\\s+");
+ if (args.length > 1) {
+ hostname = args[1];
+ }
+ PairDevice device = new PairDevice(fingerprints, hostname, false);
+ intent.putExtra(AdbManager.WIRELESS_PAIR_DEVICE_EXTRA, device);
+ mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
+ // Add the key into the keystore
+ mAdbKeyStore.setLastConnectionTime(publicKey,
+ System.currentTimeMillis());
+ sendPersistKeyStoreMessage();
+ scheduleJobToUpdateAdbKeyStore();
+ }
+ }
+
+ private void sendPairingPortToUI(int port) {
+ Intent intent = new Intent(AdbManager.WIRELESS_DEBUG_PAIRING_RESULT_ACTION);
+ intent.putExtra(AdbManager.WIRELESS_STATUS_EXTRA,
+ AdbManager.WIRELESS_STATUS_CONNECTED);
+ intent.putExtra(AdbManager.WIRELESS_DEBUG_PORT_EXTRA, port);
+ mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
+ }
+
+ private void sendPairedDevicesToUI(Map<String, PairDevice> devices) {
+ Intent intent = new Intent(AdbManager.WIRELESS_DEBUG_PAIRED_DEVICES_ACTION);
+ // Map is not serializable, so need to downcast
+ intent.putExtra(AdbManager.WIRELESS_DEVICES_EXTRA, (HashMap) devices);
+ mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
+ }
+
+ private void updateUIPairCode(String code) {
+ if (DEBUG) Slog.i(TAG, "updateUIPairCode: " + code);
+
+ Intent intent = new Intent(AdbManager.WIRELESS_DEBUG_PAIRING_RESULT_ACTION);
+ intent.putExtra(AdbManager.WIRELESS_PAIRING_CODE_EXTRA, code);
+ intent.putExtra(AdbManager.WIRELESS_STATUS_EXTRA,
+ AdbManager.WIRELESS_STATUS_PAIRING_CODE);
+ mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
+ }
}
private String getFingerprints(String key) {
@@ -570,7 +1419,34 @@
return sb.toString();
}
- private void startConfirmation(String key, String fingerprints) {
+ private void startConfirmationForNetwork(String ssid, String bssid) {
+ List<Map.Entry<String, String>> extras = new ArrayList<Map.Entry<String, String>>();
+ extras.add(new AbstractMap.SimpleEntry<String, String>("ssid", ssid));
+ extras.add(new AbstractMap.SimpleEntry<String, String>("bssid", bssid));
+ int currentUserId = ActivityManager.getCurrentUser();
+ UserInfo userInfo = UserManager.get(mContext).getUserInfo(currentUserId);
+ String componentString;
+ if (userInfo.isAdmin()) {
+ componentString = Resources.getSystem().getString(
+ com.android.internal.R.string.config_customAdbWifiNetworkConfirmationComponent);
+ } else {
+ componentString = Resources.getSystem().getString(
+ com.android.internal.R.string.config_customAdbWifiNetworkConfirmationComponent);
+ }
+ ComponentName componentName = ComponentName.unflattenFromString(componentString);
+ if (startConfirmationActivity(componentName, userInfo.getUserHandle(), extras)
+ || startConfirmationService(componentName, userInfo.getUserHandle(),
+ extras)) {
+ return;
+ }
+ Slog.e(TAG, "Unable to start customAdbWifiNetworkConfirmation[SecondaryUser]Component "
+ + componentString + " as an Activity or a Service");
+ }
+
+ private void startConfirmationForKey(String key, String fingerprints) {
+ List<Map.Entry<String, String>> extras = new ArrayList<Map.Entry<String, String>>();
+ extras.add(new AbstractMap.SimpleEntry<String, String>("key", key));
+ extras.add(new AbstractMap.SimpleEntry<String, String>("fingerprints", fingerprints));
int currentUserId = ActivityManager.getCurrentUser();
UserInfo userInfo = UserManager.get(mContext).getUserInfo(currentUserId);
String componentString;
@@ -585,9 +1461,9 @@
R.string.config_customAdbPublicKeyConfirmationSecondaryUserComponent);
}
ComponentName componentName = ComponentName.unflattenFromString(componentString);
- if (startConfirmationActivity(componentName, userInfo.getUserHandle(), key, fingerprints)
+ if (startConfirmationActivity(componentName, userInfo.getUserHandle(), extras)
|| startConfirmationService(componentName, userInfo.getUserHandle(),
- key, fingerprints)) {
+ extras)) {
return;
}
Slog.e(TAG, "unable to start customAdbPublicKeyConfirmation[SecondaryUser]Component "
@@ -598,9 +1474,9 @@
* @return true if the componentName led to an Activity that was started.
*/
private boolean startConfirmationActivity(ComponentName componentName, UserHandle userHandle,
- String key, String fingerprints) {
+ List<Map.Entry<String, String>> extras) {
PackageManager packageManager = mContext.getPackageManager();
- Intent intent = createConfirmationIntent(componentName, key, fingerprints);
+ Intent intent = createConfirmationIntent(componentName, extras);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
if (packageManager.resolveActivity(intent, PackageManager.MATCH_DEFAULT_ONLY) != null) {
try {
@@ -617,8 +1493,8 @@
* @return true if the componentName led to a Service that was started.
*/
private boolean startConfirmationService(ComponentName componentName, UserHandle userHandle,
- String key, String fingerprints) {
- Intent intent = createConfirmationIntent(componentName, key, fingerprints);
+ List<Map.Entry<String, String>> extras) {
+ Intent intent = createConfirmationIntent(componentName, extras);
try {
if (mContext.startServiceAsUser(intent, userHandle) != null) {
return true;
@@ -629,12 +1505,13 @@
return false;
}
- private Intent createConfirmationIntent(ComponentName componentName, String key,
- String fingerprints) {
+ private Intent createConfirmationIntent(ComponentName componentName,
+ List<Map.Entry<String, String>> extras) {
Intent intent = new Intent();
intent.setClassName(componentName.getPackageName(), componentName.getClassName());
- intent.putExtra("key", key);
- intent.putExtra("fingerprints", fingerprints);
+ for (Map.Entry<String, String> entry : extras) {
+ intent.putExtra(entry.getKey(), entry.getValue());
+ }
return intent;
}
@@ -717,13 +1594,22 @@
}
/**
- * When {@code enabled} is {@code true}, this allows ADB debugging and starts the ADB hanler
- * thread. When {@code enabled} is {@code false}, this disallows ADB debugging and shuts
- * down the handler thread.
+ * When {@code enabled} is {@code true}, this allows ADB debugging and starts the ADB handler
+ * thread. When {@code enabled} is {@code false}, this disallows ADB debugging for the given
+ * @{code transportType}. See {@link IAdbTransport} for all available transport types.
+ * If all transport types are disabled, the ADB handler thread will shut down.
*/
- public void setAdbEnabled(boolean enabled) {
- mHandler.sendEmptyMessage(enabled ? AdbDebuggingHandler.MESSAGE_ADB_ENABLED
- : AdbDebuggingHandler.MESSAGE_ADB_DISABLED);
+ public void setAdbEnabled(boolean enabled, byte transportType) {
+ if (transportType == AdbTransportType.USB) {
+ mHandler.sendEmptyMessage(enabled ? AdbDebuggingHandler.MESSAGE_ADB_ENABLED
+ : AdbDebuggingHandler.MESSAGE_ADB_DISABLED);
+ } else if (transportType == AdbTransportType.WIFI) {
+ mHandler.sendEmptyMessage(enabled ? AdbDebuggingHandler.MSG_ADBDWIFI_ENABLE
+ : AdbDebuggingHandler.MSG_ADBDWIFI_DISABLE);
+ } else {
+ throw new IllegalArgumentException(
+ "setAdbEnabled called with unimplemented transport type=" + transportType);
+ }
}
/**
@@ -753,6 +1639,87 @@
}
/**
+ * Allows wireless debugging on the network identified by {@code bssid} either once
+ * or always if {@code alwaysAllow} is {@code true}.
+ */
+ public void allowWirelessDebugging(boolean alwaysAllow, String bssid) {
+ Message msg = mHandler.obtainMessage(AdbDebuggingHandler.MSG_ADBWIFI_ALLOW);
+ msg.arg1 = alwaysAllow ? 1 : 0;
+ msg.obj = bssid;
+ mHandler.sendMessage(msg);
+ }
+
+ /**
+ * Denies wireless debugging connection on the last requested network.
+ */
+ public void denyWirelessDebugging() {
+ mHandler.sendEmptyMessage(AdbDebuggingHandler.MSG_ADBWIFI_DENY);
+ }
+
+ /**
+ * Returns the port adbwifi is currently opened on.
+ */
+ public int getAdbWirelessPort() {
+ AdbConnectionInfo info = getAdbConnectionInfo();
+ if (info == null) {
+ return 0;
+ }
+ return info.getPort();
+ }
+
+ /**
+ * Returns the list of paired devices.
+ */
+ public Map<String, PairDevice> getPairedDevices() {
+ AdbKeyStore keystore = new AdbKeyStore();
+ return keystore.getPairedDevices();
+ }
+
+ /**
+ * Unpair with device
+ */
+ public void unpairDevice(String fingerprint) {
+ Message message = Message.obtain(mHandler,
+ AdbDebuggingHandler.MSG_REQ_UNPAIR,
+ fingerprint);
+ mHandler.sendMessage(message);
+ }
+
+ /**
+ * Enable pairing by pairing code
+ */
+ public void enablePairingByPairingCode() {
+ mHandler.sendEmptyMessage(AdbDebuggingHandler.MSG_PAIR_PAIRING_CODE);
+ }
+
+ /**
+ * Enable pairing by pairing code
+ */
+ public void enablePairingByQrCode(String serviceName, String password) {
+ Bundle bundle = new Bundle();
+ bundle.putString("serviceName", serviceName);
+ bundle.putString("password", password);
+ Message message = Message.obtain(mHandler,
+ AdbDebuggingHandler.MSG_PAIR_QR_CODE,
+ bundle);
+ mHandler.sendMessage(message);
+ }
+
+ /**
+ * Disables pairing
+ */
+ public void disablePairing() {
+ mHandler.sendEmptyMessage(AdbDebuggingHandler.MSG_PAIRING_CANCEL);
+ }
+
+ /**
+ * Status enabled/disabled check
+ */
+ public boolean isAdbWifiEnabled() {
+ return mAdbWifiEnabled;
+ }
+
+ /**
* Sends a message to the handler to persist the keystore.
*/
private void sendPersistKeyStoreMessage() {
@@ -805,9 +1772,19 @@
private File mKeyFile;
private AtomicFile mAtomicKeyFile;
+ private List<String> mTrustedNetworks;
+ private static final int KEYSTORE_VERSION = 1;
+ private static final int MAX_SUPPORTED_KEYSTORE_VERSION = 1;
+ private static final String XML_KEYSTORE_START_TAG = "keyStore";
+ private static final String XML_ATTRIBUTE_VERSION = "version";
private static final String XML_TAG_ADB_KEY = "adbKey";
private static final String XML_ATTRIBUTE_KEY = "key";
private static final String XML_ATTRIBUTE_LAST_CONNECTION = "lastConnection";
+ // A list of trusted networks a device can always wirelessly debug on (always allow).
+ // TODO: Move trusted networks list into a different file?
+ private static final String XML_TAG_WIFI_ACCESS_POINT = "wifiAP";
+ private static final String XML_ATTRIBUTE_WIFI_BSSID = "bssid";
+
private static final String SYSTEM_KEY_FILE = "/adb_keys";
/**
@@ -834,10 +1811,49 @@
private void init() {
initKeyFile();
mKeyMap = getKeyMap();
+ mTrustedNetworks = getTrustedNetworks();
mSystemKeys = getSystemKeysFromFile(SYSTEM_KEY_FILE);
addUserKeysToKeyStore();
}
+ public void addTrustedNetwork(String bssid) {
+ mTrustedNetworks.add(bssid);
+ sendPersistKeyStoreMessage();
+ }
+
+ public Map<String, PairDevice> getPairedDevices() {
+ Map<String, PairDevice> pairedDevices = new HashMap<String, PairDevice>();
+ for (Map.Entry<String, Long> keyEntry : mKeyMap.entrySet()) {
+ String fingerprints = getFingerprints(keyEntry.getKey());
+ String hostname = "nouser@nohostname";
+ String[] args = keyEntry.getKey().split("\\s+");
+ if (args.length > 1) {
+ hostname = args[1];
+ }
+ pairedDevices.put(keyEntry.getKey(), new PairDevice(
+ hostname, fingerprints, mWifiConnectedKeys.contains(keyEntry.getKey())));
+ }
+ return pairedDevices;
+ }
+
+ public String findKeyFromFingerprint(String fingerprint) {
+ for (Map.Entry<String, Long> entry : mKeyMap.entrySet()) {
+ String f = getFingerprints(entry.getKey());
+ if (fingerprint.equals(f)) {
+ return entry.getKey();
+ }
+ }
+ return null;
+ }
+
+ public void removeKey(String key) {
+ if (mKeyMap.containsKey(key)) {
+ mKeyMap.remove(key);
+ writeKeys(mKeyMap.keySet());
+ sendPersistKeyStoreMessage();
+ }
+ }
+
/**
* Initializes the key file that will be used to persist the adb grants.
*/
@@ -907,6 +1923,78 @@
try (FileInputStream keyStream = mAtomicKeyFile.openRead()) {
XmlPullParser parser = Xml.newPullParser();
parser.setInput(keyStream, StandardCharsets.UTF_8.name());
+ // Check for supported keystore version.
+ XmlUtils.beginDocument(parser, XML_KEYSTORE_START_TAG);
+ if (parser.next() != XmlPullParser.END_DOCUMENT) {
+ String tagName = parser.getName();
+ if (tagName == null || !XML_KEYSTORE_START_TAG.equals(tagName)) {
+ Slog.e(TAG, "Expected " + XML_KEYSTORE_START_TAG + ", but got tag="
+ + tagName);
+ return keyMap;
+ }
+ int keystoreVersion = Integer.parseInt(
+ parser.getAttributeValue(null, XML_ATTRIBUTE_VERSION));
+ if (keystoreVersion > MAX_SUPPORTED_KEYSTORE_VERSION) {
+ Slog.e(TAG, "Keystore version=" + keystoreVersion
+ + " not supported (max_supported="
+ + MAX_SUPPORTED_KEYSTORE_VERSION + ")");
+ return keyMap;
+ }
+ }
+ while (parser.next() != XmlPullParser.END_DOCUMENT) {
+ String tagName = parser.getName();
+ if (tagName == null) {
+ break;
+ } else if (!tagName.equals(XML_TAG_ADB_KEY)) {
+ XmlUtils.skipCurrentTag(parser);
+ continue;
+ }
+ String key = parser.getAttributeValue(null, XML_ATTRIBUTE_KEY);
+ long connectionTime;
+ try {
+ connectionTime = Long.valueOf(
+ parser.getAttributeValue(null, XML_ATTRIBUTE_LAST_CONNECTION));
+ } catch (NumberFormatException e) {
+ Slog.e(TAG,
+ "Caught a NumberFormatException parsing the last connection time: "
+ + e);
+ XmlUtils.skipCurrentTag(parser);
+ continue;
+ }
+ keyMap.put(key, connectionTime);
+ }
+ } catch (IOException e) {
+ Slog.e(TAG, "Caught an IOException parsing the XML key file: ", e);
+ } catch (XmlPullParserException e) {
+ Slog.w(TAG, "Caught XmlPullParserException parsing the XML key file: ", e);
+ // The file could be written in a format prior to introducing keystore tag.
+ return getKeyMapBeforeKeystoreVersion();
+ }
+ return keyMap;
+ }
+
+
+ /**
+ * Returns the key map with the keys and last connection times from the key file.
+ * This implementation was prior to adding the XML_KEYSTORE_START_TAG.
+ */
+ private Map<String, Long> getKeyMapBeforeKeystoreVersion() {
+ Map<String, Long> keyMap = new HashMap<String, Long>();
+ // if the AtomicFile could not be instantiated before attempt again; if it still fails
+ // return an empty key map.
+ if (mAtomicKeyFile == null) {
+ initKeyFile();
+ if (mAtomicKeyFile == null) {
+ Slog.e(TAG, "Unable to obtain the key file, " + mKeyFile + ", for reading");
+ return keyMap;
+ }
+ }
+ if (!mAtomicKeyFile.exists()) {
+ return keyMap;
+ }
+ try (FileInputStream keyStream = mAtomicKeyFile.openRead()) {
+ XmlPullParser parser = Xml.newPullParser();
+ parser.setInput(keyStream, StandardCharsets.UTF_8.name());
XmlUtils.beginDocument(parser, XML_TAG_ADB_KEY);
while (parser.next() != XmlPullParser.END_DOCUMENT) {
String tagName = parser.getName();
@@ -937,6 +2025,63 @@
}
/**
+ * Returns the map of trusted networks from the keystore file.
+ *
+ * This was implemented in keystore version 1.
+ */
+ private List<String> getTrustedNetworks() {
+ List<String> trustedNetworks = new ArrayList<String>();
+ // if the AtomicFile could not be instantiated before attempt again; if it still fails
+ // return an empty key map.
+ if (mAtomicKeyFile == null) {
+ initKeyFile();
+ if (mAtomicKeyFile == null) {
+ Slog.e(TAG, "Unable to obtain the key file, " + mKeyFile + ", for reading");
+ return trustedNetworks;
+ }
+ }
+ if (!mAtomicKeyFile.exists()) {
+ return trustedNetworks;
+ }
+ try (FileInputStream keyStream = mAtomicKeyFile.openRead()) {
+ XmlPullParser parser = Xml.newPullParser();
+ parser.setInput(keyStream, StandardCharsets.UTF_8.name());
+ // Check for supported keystore version.
+ XmlUtils.beginDocument(parser, XML_KEYSTORE_START_TAG);
+ if (parser.next() != XmlPullParser.END_DOCUMENT) {
+ String tagName = parser.getName();
+ if (tagName == null || !XML_KEYSTORE_START_TAG.equals(tagName)) {
+ Slog.e(TAG, "Expected " + XML_KEYSTORE_START_TAG + ", but got tag="
+ + tagName);
+ return trustedNetworks;
+ }
+ int keystoreVersion = Integer.parseInt(
+ parser.getAttributeValue(null, XML_ATTRIBUTE_VERSION));
+ if (keystoreVersion > MAX_SUPPORTED_KEYSTORE_VERSION) {
+ Slog.e(TAG, "Keystore version=" + keystoreVersion
+ + " not supported (max_supported="
+ + MAX_SUPPORTED_KEYSTORE_VERSION);
+ return trustedNetworks;
+ }
+ }
+ while (parser.next() != XmlPullParser.END_DOCUMENT) {
+ String tagName = parser.getName();
+ if (tagName == null) {
+ break;
+ } else if (!tagName.equals(XML_TAG_WIFI_ACCESS_POINT)) {
+ XmlUtils.skipCurrentTag(parser);
+ continue;
+ }
+ String bssid = parser.getAttributeValue(null, XML_ATTRIBUTE_WIFI_BSSID);
+ trustedNetworks.add(bssid);
+ }
+ } catch (IOException | XmlPullParserException | NumberFormatException e) {
+ Slog.e(TAG, "Caught an exception parsing the XML key file: ", e);
+ }
+ return trustedNetworks;
+ }
+
+ /**
* Updates the keystore with keys that were previously set to be always allowed before the
* connection time of keys was tracked.
*/
@@ -972,7 +2117,7 @@
// if there is nothing in the key map then ensure any keys left in the keystore files
// are deleted as well.
filterOutOldKeys();
- if (mKeyMap.isEmpty()) {
+ if (mKeyMap.isEmpty() && mTrustedNetworks.isEmpty()) {
deleteKeyStore();
return;
}
@@ -990,6 +2135,8 @@
serializer.setOutput(keyStream, StandardCharsets.UTF_8.name());
serializer.startDocument(null, true);
+ serializer.startTag(null, XML_KEYSTORE_START_TAG);
+ serializer.attribute(null, XML_ATTRIBUTE_VERSION, String.valueOf(KEYSTORE_VERSION));
for (Map.Entry<String, Long> keyEntry : mKeyMap.entrySet()) {
serializer.startTag(null, XML_TAG_ADB_KEY);
serializer.attribute(null, XML_ATTRIBUTE_KEY, keyEntry.getKey());
@@ -997,7 +2144,12 @@
String.valueOf(keyEntry.getValue()));
serializer.endTag(null, XML_TAG_ADB_KEY);
}
-
+ for (String bssid : mTrustedNetworks) {
+ serializer.startTag(null, XML_TAG_WIFI_ACCESS_POINT);
+ serializer.attribute(null, XML_ATTRIBUTE_WIFI_BSSID, bssid);
+ serializer.endTag(null, XML_TAG_WIFI_ACCESS_POINT);
+ }
+ serializer.endTag(null, XML_KEYSTORE_START_TAG);
serializer.endDocument();
mAtomicKeyFile.finishWrite(keyStream);
} catch (IOException e) {
@@ -1058,6 +2210,7 @@
*/
public void deleteKeyStore() {
mKeyMap.clear();
+ mTrustedNetworks.clear();
deleteKeyFile();
if (mAtomicKeyFile == null) {
return;
@@ -1139,5 +2292,14 @@
return false;
}
}
+
+ /**
+ * Returns whether the specified bssid is in the list of trusted networks. This requires
+ * that the user previously allowed wireless debugging on this network and selected the
+ * option to 'Always allow'.
+ */
+ public boolean isTrustedNetwork(String bssid) {
+ return mTrustedNetworks.contains(bssid);
+ }
}
}
diff --git a/services/core/java/com/android/server/adb/AdbService.java b/services/core/java/com/android/server/adb/AdbService.java
index c125b1b..7aaf9be 100644
--- a/services/core/java/com/android/server/adb/AdbService.java
+++ b/services/core/java/com/android/server/adb/AdbService.java
@@ -15,21 +15,28 @@
*/
package com.android.server.adb;
+import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
+
+import android.annotation.NonNull;
+import android.annotation.UserIdInt;
import android.content.ContentResolver;
import android.content.Context;
+import android.content.Intent;
import android.content.pm.PackageManager;
import android.database.ContentObserver;
+import android.debug.AdbManager;
import android.debug.AdbManagerInternal;
+import android.debug.AdbTransportType;
import android.debug.IAdbManager;
import android.debug.IAdbTransport;
+import android.debug.PairDevice;
import android.hardware.usb.UsbManager;
+import android.net.Uri;
import android.os.Binder;
-import android.os.Handler;
import android.os.IBinder;
-import android.os.Looper;
-import android.os.Message;
import android.os.RemoteException;
import android.os.SystemProperties;
+import android.os.UserHandle;
import android.provider.Settings;
import android.service.adb.AdbServiceDumpProto;
import android.sysprop.AdbProperties;
@@ -38,9 +45,9 @@
import android.util.Slog;
import android.util.proto.ProtoOutputStream;
-
import com.android.internal.util.DumpUtils;
import com.android.internal.util.IndentingPrintWriter;
+import com.android.internal.util.Preconditions;
import com.android.internal.util.dump.DualDumpOutputStream;
import com.android.server.FgThread;
import com.android.server.LocalServices;
@@ -50,6 +57,8 @@
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.Collections;
+import java.util.Map;
+import java.util.concurrent.atomic.AtomicInteger;
/**
* The Android Debug Bridge (ADB) service. This controls the availability of ADB and authorization
@@ -57,6 +66,27 @@
*/
public class AdbService extends IAdbManager.Stub {
/**
+ * Adb native daemon.
+ */
+ static final String ADBD = "adbd";
+
+ /**
+ * Command to start native service.
+ */
+ static final String CTL_START = "ctl.start";
+
+ /**
+ * Command to start native service.
+ */
+ static final String CTL_STOP = "ctl.stop";
+
+ // The tcp port adb is currently using
+ AtomicInteger mConnectionPort = new AtomicInteger(-1);
+
+ private final AdbConnectionPortListener mPortListener = new AdbConnectionPortListener();
+ private AdbDebuggingManager.AdbConnectionPortPoller mConnectionPortPoller;
+
+ /**
* Manages the service lifecycle for {@code AdbService} in {@code SystemServer}.
*/
public static class Lifecycle extends SystemService {
@@ -77,7 +107,8 @@
if (phase == SystemService.PHASE_ACTIVITY_MANAGER_READY) {
mAdbService.systemReady();
} else if (phase == SystemService.PHASE_BOOT_COMPLETED) {
- mAdbService.bootCompleted();
+ FgThread.getHandler().sendMessage(obtainMessage(
+ AdbService::bootCompleted, mAdbService));
}
}
}
@@ -94,8 +125,14 @@
}
@Override
- public boolean isAdbEnabled() {
- return mAdbEnabled;
+ public boolean isAdbEnabled(byte transportType) {
+ if (transportType == AdbTransportType.USB) {
+ return mIsAdbUsbEnabled;
+ } else if (transportType == AdbTransportType.WIFI) {
+ return mIsAdbWifiEnabled;
+ }
+ throw new IllegalArgumentException(
+ "isAdbEnabled called with unimplemented transport type=" + transportType);
}
@Override
@@ -107,93 +144,100 @@
public File getAdbTempKeysFile() {
return mDebuggingManager.getAdbTempKeysFile();
}
- }
- private final class AdbHandler extends Handler {
- AdbHandler(Looper looper) {
- super(looper);
- try {
- /*
- * Use the normal bootmode persistent prop to maintain state of adb across
- * all boot modes.
- */
- mAdbEnabled = containsFunction(
- SystemProperties.get(USB_PERSISTENT_CONFIG_PROPERTY, ""),
- UsbManager.USB_FUNCTION_ADB);
-
- // register observer to listen for settings changes
- mContentResolver.registerContentObserver(
- Settings.Global.getUriFor(Settings.Global.ADB_ENABLED),
- false, new AdbSettingsObserver());
- } catch (Exception e) {
- Slog.e(TAG, "Error initializing AdbHandler", e);
- }
- }
-
- private boolean containsFunction(String functions, String function) {
- int index = functions.indexOf(function);
- if (index < 0) return false;
- if (index > 0 && functions.charAt(index - 1) != ',') return false;
- int charAfter = index + function.length();
- if (charAfter < functions.length() && functions.charAt(charAfter) != ',') return false;
- return true;
- }
-
- public void sendMessage(int what, boolean arg) {
- removeMessages(what);
- Message m = Message.obtain(this, what);
- m.arg1 = (arg ? 1 : 0);
- sendMessage(m);
+ @Override
+ public void startAdbdForTransport(byte transportType) {
+ FgThread.getHandler().sendMessage(obtainMessage(
+ AdbService::setAdbdEnabledForTransport, AdbService.this, true, transportType));
}
@Override
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case MSG_ENABLE_ADB:
- setAdbEnabled(msg.arg1 == 1);
- break;
- case MSG_BOOT_COMPLETED:
- if (mDebuggingManager != null) {
- mDebuggingManager.setAdbEnabled(mAdbEnabled);
- }
- break;
- }
+ public void stopAdbdForTransport(byte transportType) {
+ FgThread.getHandler().sendMessage(obtainMessage(
+ AdbService::setAdbdEnabledForTransport, AdbService.this, false, transportType));
}
}
+ private void initAdbState() {
+ try {
+ /*
+ * Use the normal bootmode persistent prop to maintain state of adb across
+ * all boot modes.
+ */
+ mIsAdbUsbEnabled = containsFunction(
+ SystemProperties.get(USB_PERSISTENT_CONFIG_PROPERTY, ""),
+ UsbManager.USB_FUNCTION_ADB);
+ mIsAdbWifiEnabled = "1".equals(
+ SystemProperties.get(WIFI_PERSISTENT_CONFIG_PROPERTY, "0"));
+
+ // register observer to listen for settings changes
+ mObserver = new AdbSettingsObserver();
+ mContentResolver.registerContentObserver(
+ Settings.Global.getUriFor(Settings.Global.ADB_ENABLED),
+ false, mObserver);
+ mContentResolver.registerContentObserver(
+ Settings.Global.getUriFor(Settings.Global.ADB_WIFI_ENABLED),
+ false, mObserver);
+ } catch (Exception e) {
+ Slog.e(TAG, "Error in initAdbState", e);
+ }
+ }
+
+ private static boolean containsFunction(String functions, String function) {
+ int index = functions.indexOf(function);
+ if (index < 0) return false;
+ if (index > 0 && functions.charAt(index - 1) != ',') return false;
+ int charAfter = index + function.length();
+ if (charAfter < functions.length() && functions.charAt(charAfter) != ',') return false;
+ return true;
+ }
+
private class AdbSettingsObserver extends ContentObserver {
+ private final Uri mAdbUsbUri = Settings.Global.getUriFor(Settings.Global.ADB_ENABLED);
+ private final Uri mAdbWifiUri = Settings.Global.getUriFor(Settings.Global.ADB_WIFI_ENABLED);
+
AdbSettingsObserver() {
super(null);
}
@Override
- public void onChange(boolean selfChange) {
- boolean enable = (Settings.Global.getInt(mContentResolver,
- Settings.Global.ADB_ENABLED, 0) > 0);
- mHandler.sendMessage(MSG_ENABLE_ADB, enable);
+ public void onChange(boolean selfChange, @NonNull Uri uri, @UserIdInt int userId) {
+ if (mAdbUsbUri.equals(uri)) {
+ boolean shouldEnable = (Settings.Global.getInt(mContentResolver,
+ Settings.Global.ADB_ENABLED, 0) > 0);
+ FgThread.getHandler().sendMessage(obtainMessage(
+ AdbService::setAdbEnabled, AdbService.this, shouldEnable,
+ AdbTransportType.USB));
+ } else if (mAdbWifiUri.equals(uri)) {
+ boolean shouldEnable = (Settings.Global.getInt(mContentResolver,
+ Settings.Global.ADB_WIFI_ENABLED, 0) > 0);
+ FgThread.getHandler().sendMessage(obtainMessage(
+ AdbService::setAdbEnabled, AdbService.this, shouldEnable,
+ AdbTransportType.WIFI));
+ }
}
}
private static final String TAG = "AdbService";
private static final boolean DEBUG = false;
- private static final int MSG_ENABLE_ADB = 1;
- private static final int MSG_BOOT_COMPLETED = 2;
-
/**
* The persistent property which stores whether adb is enabled or not.
* May also contain vendor-specific default functions for testing purposes.
*/
private static final String USB_PERSISTENT_CONFIG_PROPERTY = "persist.sys.usb.config";
+ private static final String WIFI_PERSISTENT_CONFIG_PROPERTY = "persist.adb.tls_server.enable";
private final Context mContext;
private final ContentResolver mContentResolver;
- private final AdbService.AdbHandler mHandler;
private final ArrayMap<IBinder, IAdbTransport> mTransports = new ArrayMap<>();
- private boolean mAdbEnabled;
+ private boolean mIsAdbUsbEnabled;
+ private boolean mIsAdbWifiEnabled;
private AdbDebuggingManager mDebuggingManager;
+ private ContentObserver mObserver;
+
private AdbService(Context context) {
mContext = context;
mContentResolver = context.getContentResolver();
@@ -204,8 +248,7 @@
mDebuggingManager = new AdbDebuggingManager(context);
}
- mHandler = new AdbHandler(FgThread.get().getLooper());
-
+ initAdbState();
LocalServices.addService(AdbManagerInternal.class, new AdbManagerInternalImpl());
}
@@ -219,7 +262,9 @@
// make sure the ADB_ENABLED setting value matches the current state
try {
Settings.Global.putInt(mContentResolver,
- Settings.Global.ADB_ENABLED, mAdbEnabled ? 1 : 0);
+ Settings.Global.ADB_ENABLED, mIsAdbUsbEnabled ? 1 : 0);
+ Settings.Global.putInt(mContentResolver,
+ Settings.Global.ADB_WIFI_ENABLED, mIsAdbWifiEnabled ? 1 : 0);
} catch (SecurityException e) {
// If UserManager.DISALLOW_DEBUGGING_FEATURES is on, that this setting can't be changed.
Slog.d(TAG, "ADB_ENABLED is restricted.");
@@ -231,12 +276,16 @@
*/
public void bootCompleted() {
if (DEBUG) Slog.d(TAG, "boot completed");
- mHandler.sendEmptyMessage(MSG_BOOT_COMPLETED);
+ if (mDebuggingManager != null) {
+ mDebuggingManager.setAdbEnabled(mIsAdbUsbEnabled, AdbTransportType.USB);
+ mDebuggingManager.setAdbEnabled(mIsAdbWifiEnabled, AdbTransportType.WIFI);
+ }
}
@Override
- public void allowDebugging(boolean alwaysAllow, String publicKey) {
+ public void allowDebugging(boolean alwaysAllow, @NonNull String publicKey) {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_DEBUGGING, null);
+ Preconditions.checkStringNotEmpty(publicKey);
if (mDebuggingManager != null) {
mDebuggingManager.allowDebugging(alwaysAllow, publicKey);
}
@@ -285,24 +334,182 @@
PackageManager.FEATURE_CAMERA_ANY);
}
- private void setAdbEnabled(boolean enable) {
- if (DEBUG) Slog.d(TAG, "setAdbEnabled(" + enable + "), mAdbEnabled=" + mAdbEnabled);
+ @Override
+ public void allowWirelessDebugging(boolean alwaysAllow, @NonNull String bssid) {
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_DEBUGGING, null);
+ Preconditions.checkStringNotEmpty(bssid);
+ if (mDebuggingManager != null) {
+ mDebuggingManager.allowWirelessDebugging(alwaysAllow, bssid);
+ }
+ }
- if (enable == mAdbEnabled) {
+ @Override
+ public void denyWirelessDebugging() {
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_DEBUGGING, null);
+ if (mDebuggingManager != null) {
+ mDebuggingManager.denyWirelessDebugging();
+ }
+ }
+
+ @Override
+ public Map<String, PairDevice> getPairedDevices() {
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_DEBUGGING, null);
+ if (mDebuggingManager != null) {
+ return mDebuggingManager.getPairedDevices();
+ }
+ return null;
+ }
+
+ @Override
+ public void unpairDevice(@NonNull String fingerprint) {
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_DEBUGGING, null);
+ Preconditions.checkStringNotEmpty(fingerprint);
+ if (mDebuggingManager != null) {
+ mDebuggingManager.unpairDevice(fingerprint);
+ }
+ }
+
+ @Override
+ public void enablePairingByPairingCode() {
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_DEBUGGING, null);
+ if (mDebuggingManager != null) {
+ mDebuggingManager.enablePairingByPairingCode();
+ }
+ }
+
+ @Override
+ public void enablePairingByQrCode(@NonNull String serviceName, @NonNull String password) {
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_DEBUGGING, null);
+ Preconditions.checkStringNotEmpty(serviceName);
+ Preconditions.checkStringNotEmpty(password);
+ if (mDebuggingManager != null) {
+ mDebuggingManager.enablePairingByQrCode(serviceName, password);
+ }
+ }
+
+ @Override
+ public void disablePairing() {
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_DEBUGGING, null);
+ if (mDebuggingManager != null) {
+ mDebuggingManager.disablePairing();
+ }
+ }
+
+ @Override
+ public int getAdbWirelessPort() {
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_DEBUGGING, null);
+ if (mDebuggingManager != null) {
+ return mDebuggingManager.getAdbWirelessPort();
+ }
+ // If ro.adb.secure=0
+ return mConnectionPort.get();
+ }
+
+ /**
+ * This listener is only used when ro.adb.secure=0. Otherwise, AdbDebuggingManager will
+ * do this.
+ */
+ class AdbConnectionPortListener implements AdbDebuggingManager.AdbConnectionPortListener {
+ public void onPortReceived(int port) {
+ if (port > 0 && port <= 65535) {
+ mConnectionPort.set(port);
+ } else {
+ mConnectionPort.set(-1);
+ // Turn off wifi debugging, since the server did not start.
+ try {
+ Settings.Global.putInt(mContentResolver,
+ Settings.Global.ADB_WIFI_ENABLED, 0);
+ } catch (SecurityException e) {
+ // If UserManager.DISALLOW_DEBUGGING_FEATURES is on, that this setting can't
+ // be changed.
+ Slog.d(TAG, "ADB_ENABLED is restricted.");
+ }
+ }
+ broadcastPortInfo(mConnectionPort.get());
+ }
+ }
+
+ private void broadcastPortInfo(int port) {
+ Intent intent = new Intent(AdbManager.WIRELESS_DEBUG_STATE_CHANGED_ACTION);
+ intent.putExtra(AdbManager.WIRELESS_STATUS_EXTRA, (port >= 0)
+ ? AdbManager.WIRELESS_STATUS_CONNECTED
+ : AdbManager.WIRELESS_STATUS_DISCONNECTED);
+ intent.putExtra(AdbManager.WIRELESS_DEBUG_PORT_EXTRA, port);
+ mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
+ Slog.i(TAG, "sent port broadcast port=" + port);
+ }
+
+ private void startAdbd() {
+ SystemProperties.set(CTL_START, ADBD);
+ }
+
+ private void stopAdbd() {
+ if (!mIsAdbUsbEnabled && !mIsAdbWifiEnabled) {
+ SystemProperties.set(CTL_STOP, ADBD);
+ }
+ }
+
+ private void setAdbdEnabledForTransport(boolean enable, byte transportType) {
+ if (transportType == AdbTransportType.USB) {
+ mIsAdbUsbEnabled = enable;
+ } else if (transportType == AdbTransportType.WIFI) {
+ mIsAdbWifiEnabled = enable;
+ }
+ if (enable) {
+ startAdbd();
+ } else {
+ stopAdbd();
+ }
+ }
+
+ private void setAdbEnabled(boolean enable, byte transportType) {
+ if (DEBUG) {
+ Slog.d(TAG, "setAdbEnabled(" + enable + "), mIsAdbUsbEnabled=" + mIsAdbUsbEnabled
+ + ", mIsAdbWifiEnabled=" + mIsAdbWifiEnabled + ", transportType="
+ + transportType);
+ }
+
+ if (transportType == AdbTransportType.USB && enable != mIsAdbUsbEnabled) {
+ mIsAdbUsbEnabled = enable;
+ } else if (transportType == AdbTransportType.WIFI && enable != mIsAdbWifiEnabled) {
+ mIsAdbWifiEnabled = enable;
+ if (mIsAdbWifiEnabled) {
+ if (!AdbProperties.secure().orElse(false) && mDebuggingManager == null) {
+ // Start adbd. If this is secure adb, then we defer enabling adb over WiFi.
+ SystemProperties.set(WIFI_PERSISTENT_CONFIG_PROPERTY, "1");
+ mConnectionPortPoller =
+ new AdbDebuggingManager.AdbConnectionPortPoller(mPortListener);
+ mConnectionPortPoller.start();
+ }
+ } else {
+ // Stop adb over WiFi.
+ SystemProperties.set(WIFI_PERSISTENT_CONFIG_PROPERTY, "0");
+ if (mConnectionPortPoller != null) {
+ mConnectionPortPoller.cancelAndWait();
+ mConnectionPortPoller = null;
+ }
+ }
+ } else {
+ // No change
return;
}
- mAdbEnabled = enable;
+
+ if (enable) {
+ startAdbd();
+ } else {
+ stopAdbd();
+ }
for (IAdbTransport transport : mTransports.values()) {
try {
- transport.onAdbEnabled(enable);
+ transport.onAdbEnabled(enable, transportType);
} catch (RemoteException e) {
Slog.w(TAG, "Unable to send onAdbEnabled to transport " + transport.toString());
}
}
if (mDebuggingManager != null) {
- mDebuggingManager.setAdbEnabled(enable);
+ mDebuggingManager.setAdbEnabled(enable, transportType);
}
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 2af04ae..53d2384 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -275,6 +275,7 @@
import android.provider.DeviceConfig.Properties;
import android.provider.Settings;
import android.server.ServerProtoEnums;
+import android.sysprop.InitProperties;
import android.sysprop.VoldProperties;
import android.text.TextUtils;
import android.text.format.DateUtils;
@@ -346,6 +347,7 @@
import com.android.server.SystemService;
import com.android.server.SystemServiceManager;
import com.android.server.ThreadPriorityBooster;
+import com.android.server.UserspaceRebootLogger;
import com.android.server.Watchdog;
import com.android.server.am.ActivityManagerServiceDumpProcessesProto.UidObserverRegistrationProto;
import com.android.server.appop.AppOpsService;
@@ -2253,6 +2255,20 @@
}
}
+ private void maybeLogUserspaceRebootEvent() {
+ if (!UserspaceRebootLogger.shouldLogUserspaceRebootEvent()) {
+ return;
+ }
+ final int userId = mUserController.getCurrentUserId();
+ if (userId != UserHandle.USER_SYSTEM) {
+ // Only log for user0.
+ return;
+ }
+ // TODO(b/148767783): should we check all profiles under user0?
+ UserspaceRebootLogger.logEventAsync(StorageManager.isUserKeyUnlocked(userId),
+ BackgroundThread.getExecutor());
+ }
+
/**
* Encapsulates global settings related to hidden API enforcement behaviour, including tracking
* the latest value via a content observer.
@@ -5306,6 +5322,12 @@
// Start looking for apps that are abusing wake locks.
Message nmsg = mHandler.obtainMessage(CHECK_EXCESSIVE_POWER_USE_MSG);
mHandler.sendMessageDelayed(nmsg, mConstants.POWER_CHECK_INTERVAL);
+ // Check if we are performing userspace reboot before setting sys.boot_completed to
+ // avoid race with init reseting sys.init.userspace_reboot.in_progress once sys
+ // .boot_completed is 1.
+ if (InitProperties.userspace_reboot_in_progress().orElse(false)) {
+ UserspaceRebootLogger.noteUserspaceRebootSuccess();
+ }
// Tell anyone interested that we are done booting!
SystemProperties.set("sys.boot_completed", "1");
@@ -5326,6 +5348,7 @@
}
}
});
+ maybeLogUserspaceRebootEvent();
mUserController.scheduleStartProfiles();
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index 972b106..af582c4 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -36,6 +36,7 @@
import android.app.IUidObserver;
import android.app.KeyguardManager;
import android.app.ProfilerInfo;
+import android.app.UserSwitchObserver;
import android.app.WaitResult;
import android.app.usage.AppStandbyInfo;
import android.app.usage.ConfigurationStats;
@@ -1708,6 +1709,42 @@
return 0;
}
+ private boolean switchUserAndWaitForComplete(int userId) throws RemoteException {
+ UserInfo currentUser = mInterface.getCurrentUser();
+ if (currentUser != null && userId == currentUser.id) {
+ // Already switched to the correct user, exit early.
+ return true;
+ }
+
+ // Register switch observer.
+ final CountDownLatch switchLatch = new CountDownLatch(1);
+ mInterface.registerUserSwitchObserver(
+ new UserSwitchObserver() {
+ @Override
+ public void onUserSwitchComplete(int newUserId) {
+ if (userId == newUserId) {
+ switchLatch.countDown();
+ }
+ }
+ }, ActivityManagerShellCommand.class.getName());
+
+ // Switch.
+ boolean switched = mInterface.switchUser(userId);
+ if (!switched) {
+ // Switching failed, don't wait for the user switch observer.
+ return false;
+ }
+
+ // Wait.
+ try {
+ switched = switchLatch.await(USER_OPERATION_TIMEOUT_MS, TimeUnit.MILLISECONDS);
+ } catch (InterruptedException e) {
+ getErrPrintWriter().println("Error: Thread interrupted unexpectedly.");
+ }
+
+ return switched;
+ }
+
int runSwitchUser(PrintWriter pw) throws RemoteException {
UserManager userManager = mInternal.mContext.getSystemService(UserManager.class);
final int userSwitchable = userManager.getUserSwitchability();
@@ -1715,9 +1752,30 @@
getErrPrintWriter().println("Error: " + userSwitchable);
return -1;
}
- String user = getNextArgRequired();
- mInterface.switchUser(Integer.parseInt(user));
- return 0;
+ boolean wait = false;
+ String opt;
+ while ((opt = getNextOption()) != null) {
+ if ("-w".equals(opt)) {
+ wait = true;
+ } else {
+ getErrPrintWriter().println("Error: unknown option: " + opt);
+ return -1;
+ }
+ }
+
+ int userId = Integer.parseInt(getNextArgRequired());
+ boolean switched;
+ if (wait) {
+ switched = switchUserAndWaitForComplete(userId);
+ } else {
+ switched = mInterface.switchUser(userId);
+ }
+ if (switched) {
+ return 0;
+ } else {
+ pw.printf("Error: Failed to switch to user %d\n", userId);
+ return 1;
+ }
}
int runGetCurrentUser(PrintWriter pw) throws RemoteException {
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index 8c60d0c..a19ac5e 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -1133,7 +1133,7 @@
@Override
public void setBatteryState(final int status, final int health, final int plugType,
final int level, final int temp, final int volt, final int chargeUAh,
- final int chargeFullUAh) {
+ final int chargeFullUAh, final long chargeTimeToFullSeconds) {
enforceCallingPermission();
// BatteryService calls us here and we may update external state. It would be wrong
@@ -1145,7 +1145,7 @@
// The battery state has not changed, so we don't need to sync external
// stats immediately.
mStats.setBatteryStateLocked(status, health, plugType, level, temp, volt,
- chargeUAh, chargeFullUAh);
+ chargeUAh, chargeFullUAh, chargeTimeToFullSeconds);
return;
}
}
@@ -1158,7 +1158,7 @@
mWorker.scheduleRunnable(() -> {
synchronized (mStats) {
mStats.setBatteryStateLocked(status, health, plugType, level, temp, volt,
- chargeUAh, chargeFullUAh);
+ chargeUAh, chargeFullUAh, chargeTimeToFullSeconds);
}
});
});
diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java
index c2652c0..8520cb7 100644
--- a/services/core/java/com/android/server/am/ProcessList.java
+++ b/services/core/java/com/android/server/am/ProcessList.java
@@ -51,6 +51,9 @@
import android.app.AppGlobals;
import android.app.AppProtoEnums;
import android.app.IApplicationThread;
+import android.app.IUidObserver;
+import android.compat.annotation.ChangeId;
+import android.compat.annotation.EnabledAfter;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@@ -103,6 +106,7 @@
import com.android.server.wm.ActivityServiceConnectionsHolder;
import com.android.server.wm.WindowManagerService;
+import dalvik.annotation.compat.VersionCodes;
import dalvik.system.VMRuntime;
import java.io.File;
@@ -280,6 +284,15 @@
// lmkd reconnect delay in msecs
private static final long LMKD_RECONNECT_DELAY_MS = 1000;
+ /**
+ * Native heap allocations will now have a non-zero tag in the most significant byte.
+ * @see <a href="https://source.android.com/devices/tech/debug/tagged-pointers">Tagged
+ * Pointers</a>
+ */
+ @ChangeId
+ @EnabledAfter(targetSdkVersion = VersionCodes.Q)
+ private static final long NATIVE_HEAP_POINTER_TAGGING = 135754954; // This is a bug id.
+
ActivityManagerService mService = null;
// To kill process groups asynchronously
@@ -1653,6 +1666,10 @@
runtimeFlags |= Zygote.USE_APP_IMAGE_STARTUP_CACHE;
}
+ if (mPlatformCompat.isChangeEnabled(NATIVE_HEAP_POINTER_TAGGING, app.info)) {
+ runtimeFlags |= Zygote.MEMORY_TAG_LEVEL_TBI;
+ }
+
String invokeWith = null;
if ((app.info.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0) {
// Debuggable apps may include a wrapper script with their library directory.
diff --git a/services/core/java/com/android/server/compat/PlatformCompat.java b/services/core/java/com/android/server/compat/PlatformCompat.java
index a5d9aa2..85b8ec6 100644
--- a/services/core/java/com/android/server/compat/PlatformCompat.java
+++ b/services/core/java/com/android/server/compat/PlatformCompat.java
@@ -234,8 +234,8 @@
@Override
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
- checkCompatChangeReadAndLogPermission();
if (!DumpUtils.checkDumpAndUsageStatsPermission(mContext, "platform_compat", pw)) return;
+ checkCompatChangeReadAndLogPermission();
mCompatConfig.dumpConfig(pw);
}
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index 7f6dc55..3c21d1a 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -216,14 +216,14 @@
* Whether to keep the connection active after rebooting, or upgrading or reinstalling. This
* only applies to {@link VpnService} connections.
*/
- private boolean mAlwaysOn = false;
+ @VisibleForTesting protected boolean mAlwaysOn = false;
/**
* Whether to disable traffic outside of this VPN even when the VPN is not connected. System
* apps can still bypass by choosing explicit networks. Has no effect if {@link mAlwaysOn} is
- * not set.
+ * not set. Applies to all types of VPNs.
*/
- private boolean mLockdown = false;
+ @VisibleForTesting protected boolean mLockdown = false;
/**
* Set of packages in addition to the VPN app itself that can access the network directly when
@@ -252,14 +252,14 @@
private final int mUserHandle;
public Vpn(Looper looper, Context context, INetworkManagementService netService,
- @UserIdInt int userHandle) {
- this(looper, context, netService, userHandle,
+ @UserIdInt int userHandle, @NonNull KeyStore keyStore) {
+ this(looper, context, netService, userHandle, keyStore,
new SystemServices(context), new Ikev2SessionCreator());
}
@VisibleForTesting
protected Vpn(Looper looper, Context context, INetworkManagementService netService,
- int userHandle, SystemServices systemServices,
+ int userHandle, @NonNull KeyStore keyStore, SystemServices systemServices,
Ikev2SessionCreator ikev2SessionCreator) {
mContext = context;
mNetd = netService;
@@ -285,7 +285,7 @@
mNetworkCapabilities.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN);
updateCapabilities(null /* defaultNetwork */);
- loadAlwaysOnPackage();
+ loadAlwaysOnPackage(keyStore);
}
/**
@@ -437,23 +437,36 @@
/**
* Checks if a VPN app supports always-on mode.
*
- * In order to support the always-on feature, an app has to
+ * <p>In order to support the always-on feature, an app has to either have an installed
+ * PlatformVpnProfile, or:
+ *
* <ul>
- * <li>target {@link VERSION_CODES#N API 24} or above, and
- * <li>not opt out through the {@link VpnService#SERVICE_META_DATA_SUPPORTS_ALWAYS_ON}
- * meta-data field.
+ * <li>target {@link VERSION_CODES#N API 24} or above, and
+ * <li>not opt out through the {@link VpnService#SERVICE_META_DATA_SUPPORTS_ALWAYS_ON}
+ * meta-data field.
* </ul>
*
* @param packageName the canonical package name of the VPN app
+ * @param keyStore the keystore instance to use for checking if the app has a Platform VPN
+ * profile installed.
* @return {@code true} if and only if the VPN app exists and supports always-on mode
*/
- public boolean isAlwaysOnPackageSupported(String packageName) {
+ public boolean isAlwaysOnPackageSupported(String packageName, @NonNull KeyStore keyStore) {
enforceSettingsPermission();
if (packageName == null) {
return false;
}
+ final long oldId = Binder.clearCallingIdentity();
+ try {
+ if (getVpnProfilePrivileged(packageName, keyStore) != null) {
+ return true;
+ }
+ } finally {
+ Binder.restoreCallingIdentity(oldId);
+ }
+
PackageManager pm = mContext.getPackageManager();
ApplicationInfo appInfo = null;
try {
@@ -485,27 +498,31 @@
}
/**
- * Configures an always-on VPN connection through a specific application.
- * This connection is automatically granted and persisted after a reboot.
+ * Configures an always-on VPN connection through a specific application. This connection is
+ * automatically granted and persisted after a reboot.
*
- * <p>The designated package should exist and declare a {@link VpnService} in its
- * manifest guarded by {@link android.Manifest.permission.BIND_VPN_SERVICE},
- * otherwise the call will fail.
+ * <p>The designated package should either have a PlatformVpnProfile installed, or declare a
+ * {@link VpnService} in its manifest guarded by {@link
+ * android.Manifest.permission.BIND_VPN_SERVICE}, otherwise the call will fail.
*
* <p>Note that this method does not check if the VPN app supports always-on mode. The check is
- * delayed to {@link #startAlwaysOnVpn()}, which is always called immediately after this
- * method in {@link android.net.IConnectivityManager#setAlwaysOnVpnPackage}.
+ * delayed to {@link #startAlwaysOnVpn()}, which is always called immediately after this method
+ * in {@link android.net.IConnectivityManager#setAlwaysOnVpnPackage}.
*
* @param packageName the package to designate as always-on VPN supplier.
* @param lockdown whether to prevent traffic outside of a VPN, for example while connecting.
* @param lockdownWhitelist packages to be whitelisted from lockdown.
+ * @param keyStore the Keystore instance to use for checking of PlatformVpnProfile(s)
* @return {@code true} if the package has been set as always-on, {@code false} otherwise.
*/
public synchronized boolean setAlwaysOnPackage(
- String packageName, boolean lockdown, List<String> lockdownWhitelist) {
+ @Nullable String packageName,
+ boolean lockdown,
+ @Nullable List<String> lockdownWhitelist,
+ @NonNull KeyStore keyStore) {
enforceControlPermissionOrInternalCaller();
- if (setAlwaysOnPackageInternal(packageName, lockdown, lockdownWhitelist)) {
+ if (setAlwaysOnPackageInternal(packageName, lockdown, lockdownWhitelist, keyStore)) {
saveAlwaysOnPackage();
return true;
}
@@ -513,20 +530,22 @@
}
/**
- * Configures an always-on VPN connection through a specific application, the same as
- * {@link #setAlwaysOnPackage}.
+ * Configures an always-on VPN connection through a specific application, the same as {@link
+ * #setAlwaysOnPackage}.
*
- * Does not perform permission checks. Does not persist any of the changes to storage.
+ * <p>Does not perform permission checks. Does not persist any of the changes to storage.
*
* @param packageName the package to designate as always-on VPN supplier.
* @param lockdown whether to prevent traffic outside of a VPN, for example while connecting.
* @param lockdownWhitelist packages to be whitelisted from lockdown. This is only used if
- * {@code lockdown} is {@code true}. Packages must not contain commas.
+ * {@code lockdown} is {@code true}. Packages must not contain commas.
+ * @param keyStore the system keystore instance to check for profiles
* @return {@code true} if the package has been set as always-on, {@code false} otherwise.
*/
@GuardedBy("this")
private boolean setAlwaysOnPackageInternal(
- String packageName, boolean lockdown, List<String> lockdownWhitelist) {
+ @Nullable String packageName, boolean lockdown,
+ @Nullable List<String> lockdownWhitelist, @NonNull KeyStore keyStore) {
if (VpnConfig.LEGACY_VPN.equals(packageName)) {
Log.w(TAG, "Not setting legacy VPN \"" + packageName + "\" as always-on.");
return false;
@@ -542,11 +561,18 @@
}
if (packageName != null) {
- // TODO: Give the minimum permission possible; if there is a Platform VPN profile, only
- // grant ACTIVATE_PLATFORM_VPN.
- // Pre-authorize new always-on VPN package. Grant the full ACTIVATE_VPN appop, allowing
- // both VpnService and Platform VPNs.
- if (!setPackageAuthorization(packageName, VpnManager.TYPE_VPN_SERVICE)) {
+ final VpnProfile profile;
+ final long oldId = Binder.clearCallingIdentity();
+ try {
+ profile = getVpnProfilePrivileged(packageName, keyStore);
+ } finally {
+ Binder.restoreCallingIdentity(oldId);
+ }
+
+ // Pre-authorize new always-on VPN package.
+ final int grantType =
+ (profile == null) ? VpnManager.TYPE_VPN_SERVICE : VpnManager.TYPE_VPN_PLATFORM;
+ if (!setPackageAuthorization(packageName, grantType)) {
return false;
}
mAlwaysOn = true;
@@ -611,11 +637,9 @@
}
}
- /**
- * Load the always-on package and lockdown config from Settings.Secure
- */
+ /** Load the always-on package and lockdown config from Settings. */
@GuardedBy("this")
- private void loadAlwaysOnPackage() {
+ private void loadAlwaysOnPackage(@NonNull KeyStore keyStore) {
final long token = Binder.clearCallingIdentity();
try {
final String alwaysOnPackage = mSystemServices.settingsSecureGetStringForUser(
@@ -626,17 +650,21 @@
Settings.Secure.ALWAYS_ON_VPN_LOCKDOWN_WHITELIST, mUserHandle);
final List<String> whitelistedPackages = TextUtils.isEmpty(whitelistString)
? Collections.emptyList() : Arrays.asList(whitelistString.split(","));
- setAlwaysOnPackageInternal(alwaysOnPackage, alwaysOnLockdown, whitelistedPackages);
+ setAlwaysOnPackageInternal(
+ alwaysOnPackage, alwaysOnLockdown, whitelistedPackages, keyStore);
} finally {
Binder.restoreCallingIdentity(token);
}
}
/**
+ * Starts the currently selected always-on VPN
+ *
+ * @param keyStore the keyStore instance for looking up PlatformVpnProfile(s)
* @return {@code true} if the service was started, the service was already connected, or there
- * was no always-on VPN to start. {@code false} otherwise.
+ * was no always-on VPN to start. {@code false} otherwise.
*/
- public boolean startAlwaysOnVpn() {
+ public boolean startAlwaysOnVpn(@NonNull KeyStore keyStore) {
final String alwaysOnPackage;
synchronized (this) {
alwaysOnPackage = getAlwaysOnPackage();
@@ -645,8 +673,8 @@
return true;
}
// Remove always-on VPN if it's not supported.
- if (!isAlwaysOnPackageSupported(alwaysOnPackage)) {
- setAlwaysOnPackage(null, false, null);
+ if (!isAlwaysOnPackageSupported(alwaysOnPackage, keyStore)) {
+ setAlwaysOnPackage(null, false, null, keyStore);
return false;
}
// Skip if the service is already established. This isn't bulletproof: it's not bound
@@ -657,10 +685,25 @@
}
}
- // Tell the OS that background services in this app need to be allowed for
- // a short time, so we can bootstrap the VPN service.
final long oldId = Binder.clearCallingIdentity();
try {
+ // Prefer VPN profiles, if any exist.
+ VpnProfile profile = getVpnProfilePrivileged(alwaysOnPackage, keyStore);
+ if (profile != null) {
+ startVpnProfilePrivileged(profile, alwaysOnPackage,
+ null /* keyStore for private key retrieval - unneeded */);
+
+ // If the above startVpnProfilePrivileged() call returns, the Ikev2VpnProfile was
+ // correctly parsed, and the VPN has started running in a different thread. The only
+ // other possibility is that the above call threw an exception, which will be
+ // caught below, and returns false (clearing the always-on VPN). Once started, the
+ // Platform VPN cannot permanently fail, and is resilient to temporary failures. It
+ // will continue retrying until shut down by the user, or always-on is toggled off.
+ return true;
+ }
+
+ // Tell the OS that background services in this app need to be allowed for
+ // a short time, so we can bootstrap the VPN service.
DeviceIdleController.LocalService idleController =
LocalServices.getService(DeviceIdleController.LocalService.class);
idleController.addPowerSaveTempWhitelistApp(Process.myUid(), alwaysOnPackage,
@@ -675,6 +718,9 @@
Log.e(TAG, "VpnService " + serviceIntent + " failed to start", e);
return false;
}
+ } catch (Exception e) {
+ Log.e(TAG, "Error starting always-on VPN", e);
+ return false;
} finally {
Binder.restoreCallingIdentity(oldId);
}
@@ -773,6 +819,7 @@
}
/** Prepare the VPN for the given package. Does not perform permission checks. */
+ @GuardedBy("this")
private void prepareInternal(String newPackage) {
long token = Binder.clearCallingIdentity();
try {
@@ -794,10 +841,10 @@
// ignore
}
mContext.unbindService(mConnection);
- mConnection = null;
+ cleanupVpnStateLocked();
} else if (mVpnRunner != null) {
+ // cleanupVpnStateLocked() is called from mVpnRunner.exit()
mVpnRunner.exit();
- mVpnRunner = null;
}
try {
@@ -1104,7 +1151,6 @@
*/
public synchronized ParcelFileDescriptor establish(VpnConfig config) {
// Check if the caller is already prepared.
- UserManager mgr = UserManager.get(mContext);
if (Binder.getCallingUid() != mOwnerUID) {
return null;
}
@@ -1118,10 +1164,7 @@
long token = Binder.clearCallingIdentity();
try {
// Restricted users are not allowed to create VPNs, they are tied to Owner
- UserInfo user = mgr.getUserInfo(mUserHandle);
- if (user.isRestricted()) {
- throw new SecurityException("Restricted users cannot establish VPNs");
- }
+ enforceNotRestrictedUser();
ResolveInfo info = AppGlobals.getPackageManager().resolveService(intent,
null, 0, mUserHandle);
@@ -1543,24 +1586,30 @@
public void interfaceRemoved(String interfaze) {
synchronized (Vpn.this) {
if (interfaze.equals(mInterface) && jniCheck(interfaze) == 0) {
- mStatusIntent = null;
- mNetworkCapabilities.setUids(null);
- mConfig = null;
- mInterface = null;
if (mConnection != null) {
mContext.unbindService(mConnection);
- mConnection = null;
- agentDisconnect();
+ cleanupVpnStateLocked();
} else if (mVpnRunner != null) {
- // agentDisconnect must be called from mVpnRunner.exit()
+ // cleanupVpnStateLocked() is called from mVpnRunner.exit()
mVpnRunner.exit();
- mVpnRunner = null;
}
}
}
}
};
+ private void cleanupVpnStateLocked() {
+ mStatusIntent = null;
+ mNetworkCapabilities.setUids(null);
+ mConfig = null;
+ mInterface = null;
+
+ // Unconditionally clear both VpnService and VpnRunner fields.
+ mVpnRunner = null;
+ mConnection = null;
+ agentDisconnect();
+ }
+
private void enforceControlPermission() {
mContext.enforceCallingPermission(Manifest.permission.CONTROL_VPN, "Unauthorized Caller");
}
@@ -1673,6 +1722,25 @@
}
/**
+ * Gets the currently running App-based VPN type
+ *
+ * @return the {@link VpnManager.VpnType}. {@link VpnManager.TYPE_VPN_NONE} if not running an
+ * app-based VPN. While VpnService-based VPNs are always app VPNs and LegacyVpn is always
+ * Settings-based, the Platform VPNs can be initiated by both apps and Settings.
+ */
+ public synchronized int getActiveAppVpnType() {
+ if (VpnConfig.LEGACY_VPN.equals(mPackage)) {
+ return VpnManager.TYPE_VPN_NONE;
+ }
+
+ if (mVpnRunner != null && mVpnRunner instanceof IkeV2VpnRunner) {
+ return VpnManager.TYPE_VPN_PLATFORM;
+ } else {
+ return VpnManager.TYPE_VPN_SERVICE;
+ }
+ }
+
+ /**
* @param uid The target uid.
*
* @return {@code true} if {@code uid} is included in one of the mBlockedUidsAsToldToNetd
@@ -1800,6 +1868,17 @@
throw new IllegalStateException("Unable to find IPv4 default gateway");
}
+ private void enforceNotRestrictedUser() {
+ Binder.withCleanCallingIdentity(() -> {
+ final UserManager mgr = UserManager.get(mContext);
+ final UserInfo user = mgr.getUserInfo(mUserHandle);
+
+ if (user.isRestricted()) {
+ throw new SecurityException("Restricted users cannot configure VPNs");
+ }
+ });
+ }
+
/**
* Start legacy VPN, controlling native daemons as needed. Creates a
* secondary thread to perform connection work, returning quickly.
@@ -1862,6 +1941,27 @@
// Prepare arguments for racoon.
String[] racoon = null;
switch (profile.type) {
+ case VpnProfile.TYPE_IKEV2_IPSEC_RSA:
+ // Secret key is still just the alias (not the actual private key). The private key
+ // is retrieved from the KeyStore during conversion of the VpnProfile to an
+ // Ikev2VpnProfile.
+ profile.ipsecSecret = Ikev2VpnProfile.PREFIX_KEYSTORE_ALIAS + privateKey;
+ profile.ipsecUserCert = userCert;
+ // Fallthrough
+ case VpnProfile.TYPE_IKEV2_IPSEC_USER_PASS:
+ profile.ipsecCaCert = caCert;
+
+ // Start VPN profile
+ startVpnProfilePrivileged(profile, VpnConfig.LEGACY_VPN, keyStore);
+ return;
+ case VpnProfile.TYPE_IKEV2_IPSEC_PSK:
+ // Ikev2VpnProfiles expect a base64-encoded preshared key.
+ profile.ipsecSecret =
+ Ikev2VpnProfile.encodeForIpsecSecret(profile.ipsecSecret.getBytes());
+
+ // Start VPN profile
+ startVpnProfilePrivileged(profile, VpnConfig.LEGACY_VPN, keyStore);
+ return;
case VpnProfile.TYPE_L2TP_IPSEC_PSK:
racoon = new String[] {
iface, profile.server, "udppsk", profile.ipsecIdentifier,
@@ -2020,7 +2120,25 @@
public abstract void run();
- protected abstract void exit();
+ /**
+ * Disconnects the NetworkAgent and cleans up all state related to the VpnRunner.
+ *
+ * <p>All outer Vpn instance state is cleaned up in cleanupVpnStateLocked()
+ */
+ protected abstract void exitVpnRunner();
+
+ /**
+ * Triggers the cleanup of the VpnRunner, and additionally cleans up Vpn instance-wide state
+ *
+ * <p>This method ensures that simple calls to exit() will always clean up global state
+ * properly.
+ */
+ protected final void exit() {
+ synchronized (Vpn.this) {
+ exitVpnRunner();
+ cleanupVpnStateLocked();
+ }
+ }
}
interface IkeV2VpnRunnerCallback {
@@ -2350,17 +2468,6 @@
}
/**
- * Triggers cleanup of outer class' state
- *
- * <p>Can be called from any thread, as it does not mutate state in the Ikev2VpnRunner.
- */
- private void cleanupVpnState() {
- synchronized (Vpn.this) {
- agentDisconnect();
- }
- }
-
- /**
* Cleans up all Ikev2VpnRunner internal state
*
* <p>This method MUST always be called on the mExecutor thread in order to ensure
@@ -2379,10 +2486,7 @@
}
@Override
- public void exit() {
- // Cleanup outer class' state immediately, otherwise race conditions may ensue.
- cleanupVpnState();
-
+ public void exitVpnRunner() {
mExecutor.execute(() -> {
shutdownVpnRunner();
});
@@ -2481,10 +2585,9 @@
/** Tears down this LegacyVpn connection */
@Override
- public void exit() {
+ public void exitVpnRunner() {
// We assume that everything is reset after stopping the daemons.
interrupt();
- agentDisconnect();
try {
mContext.unregisterReceiver(mBroadcastReceiver);
} catch (IllegalArgumentException e) {}
@@ -2757,6 +2860,7 @@
checkNotNull(keyStore, "KeyStore missing");
verifyCallingUidAndPackage(packageName);
+ enforceNotRestrictedUser();
final byte[] encodedProfile = profile.encode();
if (encodedProfile.length > MAX_VPN_PROFILE_SIZE_BYTES) {
@@ -2780,6 +2884,10 @@
return isVpnProfilePreConsented(mContext, packageName);
}
+ private boolean isCurrentIkev2VpnLocked(@NonNull String packageName) {
+ return isCurrentPreparedPackage(packageName) && mVpnRunner instanceof IkeV2VpnRunner;
+ }
+
/**
* Deletes an app-provisioned VPN profile.
*
@@ -2792,9 +2900,21 @@
checkNotNull(keyStore, "KeyStore missing");
verifyCallingUidAndPackage(packageName);
+ enforceNotRestrictedUser();
Binder.withCleanCallingIdentity(
() -> {
+ // If this profile is providing the current VPN, turn it off, disabling
+ // always-on as well if enabled.
+ if (isCurrentIkev2VpnLocked(packageName)) {
+ if (mAlwaysOn) {
+ // Will transitively call prepareInternal(VpnConfig.LEGACY_VPN).
+ setAlwaysOnPackage(null, false, null, keyStore);
+ } else {
+ prepareInternal(VpnConfig.LEGACY_VPN);
+ }
+ }
+
keyStore.delete(getProfileNameForPackage(packageName), Process.SYSTEM_UID);
});
}
@@ -2834,6 +2954,8 @@
checkNotNull(packageName, "No package name provided");
checkNotNull(keyStore, "KeyStore missing");
+ enforceNotRestrictedUser();
+
// Prepare VPN for startup
if (!prepare(packageName, null /* newPackage */, VpnManager.TYPE_VPN_PLATFORM)) {
throw new SecurityException("User consent not granted for package " + packageName);
@@ -2846,24 +2968,35 @@
throw new IllegalArgumentException("No profile found for " + packageName);
}
- startVpnProfilePrivileged(profile, packageName);
+ startVpnProfilePrivileged(profile, packageName,
+ null /* keyStore for private key retrieval - unneeded */);
});
}
- private void startVpnProfilePrivileged(
- @NonNull VpnProfile profile, @NonNull String packageName) {
- // Ensure that no other previous instance is running.
- if (mVpnRunner != null) {
- mVpnRunner.exit();
- mVpnRunner = null;
- }
+ private synchronized void startVpnProfilePrivileged(
+ @NonNull VpnProfile profile, @NonNull String packageName, @Nullable KeyStore keyStore) {
+ // Make sure VPN is prepared. This method can be called by user apps via startVpnProfile(),
+ // by the Setting app via startLegacyVpn(), or by ConnectivityService via
+ // startAlwaysOnVpn(), so this is the common place to prepare the VPN. This also has the
+ // nice property of ensuring there are no other VpnRunner instances running.
+ prepareInternal(packageName);
updateState(DetailedState.CONNECTING, "startPlatformVpn");
try {
// Build basic config
mConfig = new VpnConfig();
- mConfig.user = packageName;
- mConfig.isMetered = profile.isMetered;
+ if (VpnConfig.LEGACY_VPN.equals(packageName)) {
+ mConfig.legacy = true;
+ mConfig.session = profile.name;
+ mConfig.user = profile.key;
+
+ // TODO: Add support for configuring meteredness via Settings. Until then, use a
+ // safe default.
+ mConfig.isMetered = true;
+ } else {
+ mConfig.user = packageName;
+ mConfig.isMetered = profile.isMetered;
+ }
mConfig.startTime = SystemClock.elapsedRealtime();
mConfig.proxyInfo = profile.proxy;
@@ -2871,7 +3004,8 @@
case VpnProfile.TYPE_IKEV2_IPSEC_USER_PASS:
case VpnProfile.TYPE_IKEV2_IPSEC_PSK:
case VpnProfile.TYPE_IKEV2_IPSEC_RSA:
- mVpnRunner = new IkeV2VpnRunner(Ikev2VpnProfile.fromVpnProfile(profile));
+ mVpnRunner =
+ new IkeV2VpnRunner(Ikev2VpnProfile.fromVpnProfile(profile, keyStore));
mVpnRunner.start();
break;
default:
@@ -2899,13 +3033,13 @@
public synchronized void stopVpnProfile(@NonNull String packageName) {
checkNotNull(packageName, "No package name provided");
+ enforceNotRestrictedUser();
+
// To stop the VPN profile, the caller must be the current prepared package and must be
// running an Ikev2VpnProfile.
- if (!isCurrentPreparedPackage(packageName) && mVpnRunner instanceof IkeV2VpnRunner) {
- return;
+ if (isCurrentIkev2VpnLocked(packageName)) {
+ prepareInternal(VpnConfig.LEGACY_VPN);
}
-
- prepareInternal(VpnConfig.LEGACY_VPN);
}
/**
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
index 9a85f952..a122611 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
@@ -1092,10 +1092,11 @@
HdmiDeviceInfo avr = getAvrDeviceInfo();
if (avr != null
&& (avrAddress == avr.getLogicalAddress())
- && isConnectedToArcPort(avr.getPhysicalAddress())
- && isDirectConnectAddress(avr.getPhysicalAddress())) {
+ && isConnectedToArcPort(avr.getPhysicalAddress())) {
if (enabled) {
- return isConnected(avr.getPortId()) && isArcFeatureEnabled(avr.getPortId());
+ return isConnected(avr.getPortId())
+ && isArcFeatureEnabled(avr.getPortId())
+ && isDirectConnectAddress(avr.getPhysicalAddress());
} else {
return true;
}
diff --git a/services/core/java/com/android/server/job/controllers/JobStatus.java b/services/core/java/com/android/server/job/controllers/JobStatus.java
index e655b35..6fa9923 100644
--- a/services/core/java/com/android/server/job/controllers/JobStatus.java
+++ b/services/core/java/com/android/server/job/controllers/JobStatus.java
@@ -1502,11 +1502,11 @@
pw.println();
}
}
- if (job.getExtras() != null && !job.getExtras().maybeIsEmpty()) {
+ if (job.getExtras() != null && !job.getExtras().isDefinitelyEmpty()) {
pw.print(prefix); pw.print(" Extras: ");
pw.println(job.getExtras().toShortString());
}
- if (job.getTransientExtras() != null && !job.getTransientExtras().maybeIsEmpty()) {
+ if (job.getTransientExtras() != null && !job.getTransientExtras().isDefinitelyEmpty()) {
pw.print(prefix); pw.print(" Transient extras: ");
pw.println(job.getTransientExtras().toShortString());
}
@@ -1702,10 +1702,10 @@
job.getTriggerContentMaxDelay());
}
}
- if (job.getExtras() != null && !job.getExtras().maybeIsEmpty()) {
+ if (job.getExtras() != null && !job.getExtras().isDefinitelyEmpty()) {
job.getExtras().writeToProto(proto, JobStatusDumpProto.JobInfo.EXTRAS);
}
- if (job.getTransientExtras() != null && !job.getTransientExtras().maybeIsEmpty()) {
+ if (job.getTransientExtras() != null && !job.getTransientExtras().isDefinitelyEmpty()) {
job.getTransientExtras().writeToProto(proto, JobStatusDumpProto.JobInfo.TRANSIENT_EXTRAS);
}
if (job.getClipData() != null) {
diff --git a/services/core/java/com/android/server/net/NetworkStatsService.java b/services/core/java/com/android/server/net/NetworkStatsService.java
index a7f8618..896bf67 100644
--- a/services/core/java/com/android/server/net/NetworkStatsService.java
+++ b/services/core/java/com/android/server/net/NetworkStatsService.java
@@ -1253,7 +1253,7 @@
final NetworkStats uidSnapshot = getNetworkStatsUidDetail(INTERFACES_ALL);
Trace.traceEnd(TRACE_TAG_NETWORK);
Trace.traceBegin(TRACE_TAG_NETWORK, "snapshotXt");
- final NetworkStats xtSnapshot = getNetworkStatsXt();
+ final NetworkStats xtSnapshot = readNetworkStatsSummaryXt();
Trace.traceEnd(TRACE_TAG_NETWORK);
Trace.traceBegin(TRACE_TAG_NETWORK, "snapshotDev");
final NetworkStats devSnapshot = readNetworkStatsSummaryDev();
@@ -1742,18 +1742,6 @@
mUseBpfTrafficStats);
uidSnapshot.combineAllValues(tetherSnapshot);
- final TelephonyManager telephonyManager = (TelephonyManager) mContext.getSystemService(
- Context.TELEPHONY_SERVICE);
-
- // fold video calling data usage stats into uid snapshot
- final NetworkStats vtStats = telephonyManager.getVtDataUsage(STATS_PER_UID);
- if (vtStats != null) {
- vtStats.filter(UID_ALL, ifaces, TAG_ALL);
- mStatsFactory.apply464xlatAdjustments(uidSnapshot, vtStats,
- mUseBpfTrafficStats);
- uidSnapshot.combineAllValues(vtStats);
- }
-
// get a stale copy of uid stats snapshot provided by providers.
final NetworkStats providerStats = getNetworkStatsFromProviders(STATS_PER_UID);
providerStats.filter(UID_ALL, ifaces, TAG_ALL);
@@ -1766,24 +1754,6 @@
}
/**
- * Return snapshot of current XT statistics with video calling data usage statistics.
- */
- private NetworkStats getNetworkStatsXt() throws RemoteException {
- final NetworkStats xtSnapshot = readNetworkStatsSummaryXt();
-
- final TelephonyManager telephonyManager = (TelephonyManager) mContext.getSystemService(
- Context.TELEPHONY_SERVICE);
-
- // Merge video calling data usage into XT
- final NetworkStats vtSnapshot = telephonyManager.getVtDataUsage(STATS_PER_IFACE);
- if (vtSnapshot != null) {
- xtSnapshot.combineAllValues(vtSnapshot);
- }
-
- return xtSnapshot;
- }
-
- /**
* Return snapshot of current tethering statistics. Will return empty
* {@link NetworkStats} if any problems are encountered.
*/
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index d8f5dfb..709811e 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -9764,8 +9764,8 @@
}
@Override
- public void notifyDexLoad(String loadingPackageName, List<String> classLoaderNames,
- List<String> classPaths, String loaderIsa) {
+ public void notifyDexLoad(String loadingPackageName, Map<String, String> classLoaderContextMap,
+ String loaderIsa) {
int userId = UserHandle.getCallingUserId();
ApplicationInfo ai = getApplicationInfo(loadingPackageName, /*flags*/ 0, userId);
if (ai == null) {
@@ -9773,7 +9773,7 @@
+ loadingPackageName + ", user=" + userId);
return;
}
- mDexManager.notifyDexLoad(ai, classLoaderNames, classPaths, loaderIsa, userId);
+ mDexManager.notifyDexLoad(ai, classLoaderContextMap, loaderIsa, userId);
}
@Override
diff --git a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
index 3be51c5..98c6c6d 100644
--- a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
+++ b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java
@@ -562,6 +562,9 @@
android.provider.Settings.Global.putStringForUser(cr,
android.provider.Settings.Global.ADB_ENABLED, "0",
userId);
+ android.provider.Settings.Global.putStringForUser(cr,
+ android.provider.Settings.Global.ADB_WIFI_ENABLED, "0",
+ userId);
}
}
break;
@@ -702,6 +705,7 @@
break;
case android.provider.Settings.Global.ADB_ENABLED:
+ case android.provider.Settings.Global.ADB_WIFI_ENABLED:
if ("0".equals(value)) {
return false;
}
diff --git a/services/core/java/com/android/server/pm/dex/DexManager.java b/services/core/java/com/android/server/pm/dex/DexManager.java
index 9e86a4b..441fa75 100644
--- a/services/core/java/com/android/server/pm/dex/DexManager.java
+++ b/services/core/java/com/android/server/pm/dex/DexManager.java
@@ -44,6 +44,8 @@
import com.android.server.pm.PackageManagerService;
import com.android.server.pm.PackageManagerServiceUtils;
+import dalvik.system.VMRuntime;
+
import java.io.File;
import java.io.IOException;
import java.util.Arrays;
@@ -143,22 +145,15 @@
* return as fast as possible.
*
* @param loadingAppInfo the package performing the load
- * @param classLoadersNames the names of the class loaders present in the loading chain. The
- * list encodes the class loader chain in the natural order. The first class loader has
- * the second one as its parent and so on. The dex files present in the class path of the
- * first class loader will be recorded in the usage file.
- * @param classPaths the class paths corresponding to the class loaders names from
- * {@param classLoadersNames}. The the first element corresponds to the first class loader
- * and so on. A classpath is represented as a list of dex files separated by
- * {@code File.pathSeparator}, or null if the class loader's classpath is not known.
- * The dex files found in the first class path will be recorded in the usage file.
+ * @param classLoaderContextMap a map from file paths to dex files that have been loaded to
+ * the class loader context that was used to load them.
* @param loaderIsa the ISA of the app loading the dex files
* @param loaderUserId the user id which runs the code loading the dex files
*/
- public void notifyDexLoad(ApplicationInfo loadingAppInfo, List<String> classLoadersNames,
- List<String> classPaths, String loaderIsa, int loaderUserId) {
+ public void notifyDexLoad(ApplicationInfo loadingAppInfo,
+ Map<String, String> classLoaderContextMap, String loaderIsa, int loaderUserId) {
try {
- notifyDexLoadInternal(loadingAppInfo, classLoadersNames, classPaths, loaderIsa,
+ notifyDexLoadInternal(loadingAppInfo, classLoaderContextMap, loaderIsa,
loaderUserId);
} catch (Exception e) {
Slog.w(TAG, "Exception while notifying dex load for package " +
@@ -168,46 +163,23 @@
@VisibleForTesting
/*package*/ void notifyDexLoadInternal(ApplicationInfo loadingAppInfo,
- List<String> classLoaderNames, List<String> classPaths, String loaderIsa,
+ Map<String, String> classLoaderContextMap, String loaderIsa,
int loaderUserId) {
- if (classLoaderNames.size() != classPaths.size()) {
- Slog.wtf(TAG, "Bad call to noitfyDexLoad: args have different size");
+ if (classLoaderContextMap == null) {
return;
}
- if (classLoaderNames.isEmpty()) {
+ if (classLoaderContextMap.isEmpty()) {
Slog.wtf(TAG, "Bad call to notifyDexLoad: class loaders list is empty");
return;
}
if (!PackageManagerServiceUtils.checkISA(loaderIsa)) {
- Slog.w(TAG, "Loading dex files " + classPaths + " in unsupported ISA: " +
- loaderIsa + "?");
+ Slog.w(TAG, "Loading dex files " + classLoaderContextMap.keySet()
+ + " in unsupported ISA: " + loaderIsa + "?");
return;
}
- // The first classpath should never be null because the first classloader
- // should always be an instance of BaseDexClassLoader.
- String firstClassPath = classPaths.get(0);
- if (firstClassPath == null) {
- return;
- }
- // The classpath is represented as a list of dex files separated by File.pathSeparator.
- String[] dexPathsToRegister = firstClassPath.split(File.pathSeparator);
-
- // Encode the class loader contexts for the dexPathsToRegister.
- String[] classLoaderContexts = DexoptUtils.processContextForDexLoad(
- classLoaderNames, classPaths);
-
- // A null classLoaderContexts means that there are unsupported class loaders in the
- // chain.
- if (classLoaderContexts == null) {
- if (DEBUG) {
- Slog.i(TAG, loadingAppInfo.packageName +
- " uses unsupported class loader in " + classLoaderNames);
- }
- }
-
- int dexPathIndex = 0;
- for (String dexPath : dexPathsToRegister) {
+ for (Map.Entry<String, String> mapping : classLoaderContextMap.entrySet()) {
+ String dexPath = mapping.getKey();
// Find the owning package name.
DexSearchResult searchResult = getDexPackage(loadingAppInfo, dexPath, loaderUserId);
@@ -229,7 +201,6 @@
// If the dex file is the primary apk (or a split) and not isUsedByOtherApps
// do not record it. This case does not bring any new usable information
// and can be safely skipped.
- dexPathIndex++;
continue;
}
@@ -239,13 +210,13 @@
searchResult.mOwningPackageName, loadingAppInfo.packageName);
}
- if (classLoaderContexts != null) {
-
+ String classLoaderContext = mapping.getValue();
+ if (classLoaderContext != null
+ && VMRuntime.isValidClassLoaderContext(classLoaderContext)) {
// Record dex file usage. If the current usage is a new pattern (e.g. new
// secondary, or UsedByOtherApps), record will return true and we trigger an
// async write to disk to make sure we don't loose the data in case of a reboot.
- String classLoaderContext = classLoaderContexts[dexPathIndex];
if (mPackageDexUsage.record(searchResult.mOwningPackageName,
dexPath, loaderUserId, loaderIsa, isUsedByOtherApps, primaryOrSplit,
loadingAppInfo.packageName, classLoaderContext)) {
@@ -259,7 +230,6 @@
Slog.i(TAG, "Could not find owning package for dex file: " + dexPath);
}
}
- dexPathIndex++;
}
}
diff --git a/services/core/java/com/android/server/pm/dex/PackageDexUsage.java b/services/core/java/com/android/server/pm/dex/PackageDexUsage.java
index e68c238..08763e7 100644
--- a/services/core/java/com/android/server/pm/dex/PackageDexUsage.java
+++ b/services/core/java/com/android/server/pm/dex/PackageDexUsage.java
@@ -83,8 +83,9 @@
"=UnknownClassLoaderContext=";
// The marker used for unsupported class loader contexts (no longer written, may occur in old
- // files so discarded on read).
- private static final String UNSUPPORTED_CLASS_LOADER_CONTEXT =
+ // files so discarded on read). Note: this matches
+ // ClassLoaderContext::kUnsupportedClassLoaderContextEncoding in the runtime.
+ /*package*/ static final String UNSUPPORTED_CLASS_LOADER_CONTEXT =
"=UnsupportedClassLoaderContext=";
/**
@@ -133,6 +134,9 @@
if (classLoaderContext == null) {
throw new IllegalArgumentException("Null classLoaderContext");
}
+ if (classLoaderContext.equals(UNSUPPORTED_CLASS_LOADER_CONTEXT)) {
+ return false;
+ }
synchronized (mPackageUseInfoMap) {
PackageUseInfo packageUseInfo = mPackageUseInfoMap.get(owningPackageName);
@@ -843,10 +847,11 @@
boolean updateLoadingPackages = mLoadingPackages.addAll(dexUseInfo.mLoadingPackages);
String oldClassLoaderContext = mClassLoaderContext;
- if (UNKNOWN_CLASS_LOADER_CONTEXT.equals(mClassLoaderContext)) {
+ if (isUnknownOrUnsupportedContext(mClassLoaderContext)) {
// Can happen if we read a previous version.
mClassLoaderContext = dexUseInfo.mClassLoaderContext;
- } else if (!Objects.equals(mClassLoaderContext, dexUseInfo.mClassLoaderContext)) {
+ } else if (!isUnknownOrUnsupportedContext(dexUseInfo.mClassLoaderContext)
+ && !Objects.equals(mClassLoaderContext, dexUseInfo.mClassLoaderContext)) {
// We detected a context change.
mClassLoaderContext = VARIABLE_CLASS_LOADER_CONTEXT;
}
@@ -857,6 +862,13 @@
|| !Objects.equals(oldClassLoaderContext, mClassLoaderContext);
}
+ private static boolean isUnknownOrUnsupportedContext(String context) {
+ // TODO: Merge UNKNOWN_CLASS_LOADER_CONTEXT & UNSUPPORTED_CLASS_LOADER_CONTEXT cases
+ // into UNSUPPORTED_CLASS_LOADER_CONTEXT.
+ return UNKNOWN_CLASS_LOADER_CONTEXT.equals(context)
+ || UNSUPPORTED_CLASS_LOADER_CONTEXT.equals(context);
+ }
+
public boolean isUsedByOtherApps() {
return mIsUsedByOtherApps;
}
@@ -878,7 +890,7 @@
public boolean isUnknownClassLoaderContext() {
// The class loader context may be unknown if we loaded the data from a previous version
// which didn't save the context.
- return UNKNOWN_CLASS_LOADER_CONTEXT.equals(mClassLoaderContext);
+ return isUnknownOrUnsupportedContext(mClassLoaderContext);
}
public boolean isVariableClassLoaderContext() {
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index 3a7604a..26a623f 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -23,6 +23,7 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.app.ActivityManager;
import android.app.SynchronousUserSwitchObserver;
@@ -91,6 +92,7 @@
import com.android.server.ServiceThread;
import com.android.server.SystemService;
import com.android.server.UiThread;
+import com.android.server.UserspaceRebootLogger;
import com.android.server.Watchdog;
import com.android.server.am.BatteryStatsService;
import com.android.server.lights.LightsManager;
@@ -2834,7 +2836,10 @@
}
private void shutdownOrRebootInternal(final @HaltMode int haltMode, final boolean confirm,
- final String reason, boolean wait) {
+ @Nullable final String reason, boolean wait) {
+ if (PowerManager.REBOOT_USERSPACE.equals(reason)) {
+ UserspaceRebootLogger.noteUserspaceRebootWasRequested();
+ }
if (mHandler == null || !mSystemReady) {
if (RescueParty.isAttemptingFactoryReset()) {
// If we're stuck in a really low-level reboot loop, and a
@@ -4649,7 +4654,7 @@
* @param wait If true, this call waits for the reboot to complete and does not return.
*/
@Override // Binder call
- public void reboot(boolean confirm, String reason, boolean wait) {
+ public void reboot(boolean confirm, @Nullable String reason, boolean wait) {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.REBOOT, null);
if (PowerManager.REBOOT_RECOVERY.equals(reason)
|| PowerManager.REBOOT_RECOVERY_UPDATE.equals(reason)) {
diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp
index 81b42da..153b006 100644
--- a/services/core/jni/Android.bp
+++ b/services/core/jni/Android.bp
@@ -21,6 +21,7 @@
"BroadcastRadio/convert.cpp",
"BroadcastRadio/regions.cpp",
"com_android_server_AlarmManagerService.cpp",
+ "com_android_server_adb_AdbDebuggingManager.cpp",
"com_android_server_am_BatteryStatsService.cpp",
"com_android_server_connectivity_Vpn.cpp",
"com_android_server_ConsumerIrService.cpp",
@@ -69,6 +70,8 @@
cc_defaults {
name: "libservices.core-libs",
shared_libs: [
+ "libadb_pairing_server",
+ "libadb_pairing_connection",
"libandroid_runtime",
"libandroidfw",
"libaudioclient",
diff --git a/services/core/jni/com_android_server_adb_AdbDebuggingManager.cpp b/services/core/jni/com_android_server_adb_AdbDebuggingManager.cpp
new file mode 100644
index 0000000..aa50eec
--- /dev/null
+++ b/services/core/jni/com_android_server_adb_AdbDebuggingManager.cpp
@@ -0,0 +1,187 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+#define LOG_TAG "AdbDebuggingManager-JNI"
+
+#define LOG_NDEBUG 0
+
+#include <algorithm>
+#include <condition_variable>
+#include <mutex>
+#include <optional>
+#include <random>
+#include <string>
+#include <vector>
+
+#include <adb/pairing/pairing_server.h>
+#include <android-base/properties.h>
+#include <utils/Log.h>
+
+#include <nativehelper/JNIHelp.h>
+#include "jni.h"
+
+namespace android {
+
+// ----------------------------------------------------------------------------
+namespace {
+
+template <class T, class N>
+class JSmartWrapper {
+public:
+ JSmartWrapper(JNIEnv* env, T* jData) :
+ mEnv(env),
+ mJData(jData) {
+ }
+
+ virtual ~JSmartWrapper() = default;
+
+ const N* data() const {
+ return mRawData;
+ }
+
+ jsize size() const {
+ return mSize;
+ }
+
+protected:
+ N* mRawData = nullptr;
+ JNIEnv* mEnv = nullptr;
+ T* mJData = nullptr;
+ jsize mSize = 0;
+}; // JSmartWrapper
+
+class JStringUTFWrapper : public JSmartWrapper<jstring, const char> {
+public:
+ explicit JStringUTFWrapper(JNIEnv* env, jstring* str)
+ : JSmartWrapper(env, str) {
+ mRawData = env->GetStringUTFChars(*str, NULL);
+ mSize = env->GetStringUTFLength(*str);
+ }
+
+ virtual ~JStringUTFWrapper() {
+ if (data()) {
+ mEnv->ReleaseStringUTFChars(*mJData, mRawData);
+ }
+ }
+}; // JStringUTFWrapper
+
+struct ServerDeleter {
+ void operator()(PairingServerCtx* p) {
+ pairing_server_destroy(p);
+ }
+};
+using PairingServerPtr = std::unique_ptr<PairingServerCtx, ServerDeleter>;
+struct PairingResultWaiter {
+ std::mutex mutex_;
+ std::condition_variable cv_;
+ std::optional<bool> is_valid_;
+ PeerInfo peer_info_;
+
+ static void ResultCallback(const PeerInfo* peer_info,
+ void* opaque) {
+ auto* p = reinterpret_cast<PairingResultWaiter*>(opaque);
+ {
+ std::unique_lock<std::mutex> lock(p->mutex_);
+ if (peer_info) {
+ memcpy(&(p->peer_info_), peer_info, sizeof(PeerInfo));
+ }
+ p->is_valid_ = (peer_info != nullptr);
+ }
+ p->cv_.notify_one();
+ }
+};
+
+PairingServerPtr sServer;
+std::unique_ptr<PairingResultWaiter> sWaiter;
+} // namespace
+
+static jint native_pairing_start(JNIEnv* env,
+ jobject thiz,
+ jstring guid,
+ jstring password) {
+ // Server-side only sends its GUID on success.
+ PeerInfo system_info = {};
+ system_info.type = ADB_DEVICE_GUID;
+ JStringUTFWrapper guidWrapper(env, &guid);
+ memcpy(system_info.data, guidWrapper.data(), guidWrapper.size());
+
+ JStringUTFWrapper passwordWrapper(env, &password);
+
+ // Create the pairing server
+ sServer = PairingServerPtr(
+ pairing_server_new_no_cert(reinterpret_cast<const uint8_t*>(passwordWrapper.data()),
+ passwordWrapper.size(),
+ &system_info,
+ 0));
+
+ sWaiter.reset(new PairingResultWaiter);
+ uint16_t port = pairing_server_start(sServer.get(), sWaiter->ResultCallback, sWaiter.get());
+ if (port == 0) {
+ ALOGE("Failed to start pairing server");
+ return -1;
+ }
+
+ return port;
+}
+
+static void native_pairing_cancel(JNIEnv* /* env */,
+ jclass /* clazz */) {
+ if (sServer != nullptr) {
+ sServer.reset();
+ }
+}
+
+static jboolean native_pairing_wait(JNIEnv* env,
+ jobject thiz) {
+ ALOGI("Waiting for pairing server to complete");
+ std::unique_lock<std::mutex> lock(sWaiter->mutex_);
+ if (!sWaiter->is_valid_.has_value()) {
+ sWaiter->cv_.wait(lock, [&]() { return sWaiter->is_valid_.has_value(); });
+ }
+ if (!*(sWaiter->is_valid_)) {
+ return JNI_FALSE;
+ }
+
+ std::string peer_public_key = reinterpret_cast<char*>(sWaiter->peer_info_.data);
+ // Write to PairingThread's member variables
+ jclass clazz = env->GetObjectClass(thiz);
+ jfieldID mPublicKey = env->GetFieldID(clazz, "mPublicKey", "Ljava/lang/String;");
+ jstring jpublickey = env->NewStringUTF(peer_public_key.c_str());
+ env->SetObjectField(thiz, mPublicKey, jpublickey);
+ return JNI_TRUE;
+}
+
+// ----------------------------------------------------------------------------
+
+static const JNINativeMethod gPairingThreadMethods[] = {
+ /* name, signature, funcPtr */
+ { "native_pairing_start", "(Ljava/lang/String;Ljava/lang/String;)I",
+ (void*) native_pairing_start },
+ { "native_pairing_cancel", "()V",
+ (void*) native_pairing_cancel },
+ { "native_pairing_wait", "()Z",
+ (void*) native_pairing_wait },
+};
+
+int register_android_server_AdbDebuggingManager(JNIEnv* env) {
+ int res = jniRegisterNativeMethods(env, "com/android/server/adb/AdbDebuggingManager$PairingThread",
+ gPairingThreadMethods, NELEM(gPairingThreadMethods));
+ (void) res; // Faked use when LOG_NDEBUG.
+ LOG_FATAL_IF(res < 0, "Unable to register native methods.");
+ return 0;
+}
+
+} /* namespace android */
diff --git a/services/core/jni/onload.cpp b/services/core/jni/onload.cpp
index 692c9d2..657920e 100644
--- a/services/core/jni/onload.cpp
+++ b/services/core/jni/onload.cpp
@@ -56,6 +56,7 @@
int register_android_server_security_VerityUtils(JNIEnv* env);
int register_android_server_am_AppCompactor(JNIEnv* env);
int register_android_server_am_LowMemDetector(JNIEnv* env);
+int register_android_server_AdbDebuggingManager(JNIEnv* env);
};
using namespace android;
@@ -105,5 +106,6 @@
register_android_server_security_VerityUtils(env);
register_android_server_am_AppCompactor(env);
register_android_server_am_LowMemDetector(env);
+ register_android_server_AdbDebuggingManager(env);
return JNI_VERSION_1_4;
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 952a64c..03f64fc 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -403,7 +403,7 @@
* System property whose value is either "true" or "false", indicating whether
* device owner is present.
*/
- private static final String PROPERTY_DEVICE_OWNER_PRESENT = "ro.device_owner";
+ private static final String PROPERTY_DEVICE_OWNER_PRESENT = "ro.organization_owned";
private static final int STATUS_BAR_DISABLE_MASK =
StatusBarManager.DISABLE_EXPAND |
@@ -434,6 +434,7 @@
GLOBAL_SETTINGS_WHITELIST = new ArraySet<>();
GLOBAL_SETTINGS_WHITELIST.add(Settings.Global.ADB_ENABLED);
+ GLOBAL_SETTINGS_WHITELIST.add(Settings.Global.ADB_WIFI_ENABLED);
GLOBAL_SETTINGS_WHITELIST.add(Settings.Global.AUTO_TIME);
GLOBAL_SETTINGS_WHITELIST.add(Settings.Global.AUTO_TIME_ZONE);
GLOBAL_SETTINGS_WHITELIST.add(Settings.Global.DATA_ROAMING);
@@ -2475,11 +2476,11 @@
}
if (!mInjector.systemPropertiesGet(PROPERTY_DEVICE_OWNER_PRESENT, "").isEmpty()) {
- Slog.w(LOG_TAG, "Trying to set ro.device_owner, but it has already been set?");
+ Slog.w(LOG_TAG, "Trying to set ro.organization_owned, but it has already been set?");
} else {
final String value = Boolean.toString(hasDeviceOwner);
mInjector.systemPropertiesSet(PROPERTY_DEVICE_OWNER_PRESENT, value);
- Slog.i(LOG_TAG, "Set ro.device_owner property to " + value);
+ Slog.i(LOG_TAG, "Set ro.organization_owned property to " + value);
}
}
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index b93365a..670ec31 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -41,7 +41,6 @@
import android.database.sqlite.SQLiteGlobal;
import android.hardware.display.DisplayManagerInternal;
import android.net.ConnectivityModuleConnector;
-import android.net.ITetheringConnector;
import android.net.NetworkStackClient;
import android.os.BaseBundle;
import android.os.Binder;
@@ -286,6 +285,8 @@
private static final String CONTENT_SUGGESTIONS_SERVICE_CLASS =
"com.android.server.contentsuggestions.ContentSuggestionsManagerService";
+ private static final String TETHERING_CONNECTOR_CLASS = "android.net.ITetheringConnector";
+
private static final String PERSISTENT_DATA_BLOCK_PROP = "ro.frp.pst";
private static final String UNCRYPT_PACKAGE_FILE = "/cache/recovery/uncrypt_file";
@@ -938,7 +939,6 @@
false);
boolean disableCameraService = SystemProperties.getBoolean("config.disable_cameraservice",
false);
- boolean disableSlices = SystemProperties.getBoolean("config.disable_slices", false);
boolean enableLeftyService = SystemProperties.getBoolean("config.enable_lefty", false);
boolean isEmulator = SystemProperties.get("ro.kernel.qemu").equals("1");
@@ -1910,7 +1910,7 @@
traceEnd();
}
- if (!disableSlices) {
+ if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_SLICES_DISABLED)) {
traceBeginAndSlog("StartSliceManagerService");
mSystemServiceManager.startService(SLICE_MANAGER_SERVICE_CLASS);
traceEnd();
@@ -2226,7 +2226,7 @@
try {
// TODO: hide implementation details, b/146312721.
ConnectivityModuleConnector.getInstance().startModuleService(
- ITetheringConnector.class.getName(),
+ TETHERING_CONNECTOR_CLASS,
PERMISSION_MAINLINE_NETWORK_STACK, service -> {
ServiceManager.addService(Context.TETHERING_SERVICE, service,
false /* allowIsolated */,
diff --git a/services/robotests/Android.bp b/services/robotests/Android.bp
index 3ce514a..a08b3e7 100644
--- a/services/robotests/Android.bp
+++ b/services/robotests/Android.bp
@@ -26,6 +26,8 @@
"services.core",
"services.net",
],
+
+ libs: ["ike-stubs"],
}
//##################################################################
diff --git a/services/robotests/backup/Android.bp b/services/robotests/backup/Android.bp
index 9d384e9..6fcc242 100644
--- a/services/robotests/backup/Android.bp
+++ b/services/robotests/backup/Android.bp
@@ -28,6 +28,8 @@
"services.core",
"services.net",
],
+
+ libs: ["ike-stubs"],
}
//##################################################################
diff --git a/services/tests/servicestests/src/com/android/server/adb/AdbDebuggingManagerTest.java b/services/tests/servicestests/src/com/android/server/adb/AdbDebuggingManagerTest.java
index d4182f3..c687ede 100644
--- a/services/tests/servicestests/src/com/android/server/adb/AdbDebuggingManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/adb/AdbDebuggingManagerTest.java
@@ -672,6 +672,31 @@
connectionTime2, mKeyStore.getLastConnectionTime(TEST_KEY_2));
}
+ @Test
+ public void testAdbKeyStore_removeKey() throws Exception {
+ // Accept the test key with the 'Always allow' option selected.
+ runAdbTest(TEST_KEY_1, true, true, false);
+ runAdbTest(TEST_KEY_2, true, true, false);
+
+ // Set the connection time to 0 to restore the original behavior.
+ setAllowedConnectionTime(0);
+
+ // Verify that the key is in the adb_keys file to ensure subsequent connections are
+ // automatically allowed by adbd.
+ persistKeyStore();
+ assertTrue("The key was not in the adb_keys file after persisting the keystore",
+ isKeyInFile(TEST_KEY_1, mAdbKeyFile));
+ assertTrue("The key was not in the adb_keys file after persisting the keystore",
+ isKeyInFile(TEST_KEY_2, mAdbKeyFile));
+
+ // Now remove one of the keys and make sure the other key is still there
+ mKeyStore.removeKey(TEST_KEY_1);
+ assertFalse("The key was still in the adb_keys file after removing the key",
+ isKeyInFile(TEST_KEY_1, mAdbKeyFile));
+ assertTrue("The key was not in the adb_keys file after removing a different key",
+ isKeyInFile(TEST_KEY_2, mAdbKeyFile));
+ }
+
/**
* Runs an adb test with the provided configuration.
*
diff --git a/services/tests/servicestests/src/com/android/server/pm/dex/DexManagerTests.java b/services/tests/servicestests/src/com/android/server/pm/dex/DexManagerTests.java
index a4ba056..cb20b65 100644
--- a/services/tests/servicestests/src/com/android/server/pm/dex/DexManagerTests.java
+++ b/services/tests/servicestests/src/com/android/server/pm/dex/DexManagerTests.java
@@ -532,6 +532,61 @@
assertHasDclInfo(mBarUser0, mBarUser0, secondaries);
}
+ @Test
+ public void testPrimaryAndSecondaryDexLoad() {
+ // Foo loads both primary and secondary dexes
+ List<String> fooSecondaries = mFooUser0.getSecondaryDexPaths();
+ List<String> fooDexes = new ArrayList<>(mFooUser0.getBaseAndSplitDexPaths());
+ int primaryCount = fooDexes.size();
+ fooDexes.addAll(fooSecondaries);
+
+ notifyDexLoad(mFooUser0, fooDexes, mUser0);
+
+ PackageUseInfo pui = getPackageUseInfo(mFooUser0);
+ assertIsUsedByOtherApps(mFooUser0, pui, false);
+ assertEquals(fooSecondaries.size(), pui.getDexUseInfoMap().size());
+
+ // Below we want to verify that the secondary dex files within fooDexes have been correctly
+ // reported and their class loader contexts were correctly recorded.
+ //
+ // In order to achieve this we first use DexoptUtils.processContextForDexLoad to compute the
+ // class loader contexts for all the dex files.
+ String[] allClassLoaderContexts = DexoptUtils.processContextForDexLoad(
+ Arrays.asList(mFooUser0.mClassLoader),
+ Arrays.asList(String.join(File.pathSeparator, fooDexes)));
+ // Next we filter out the class loader contexts corresponding to non-secondary dex files.
+ String[] secondaryClassLoaderContexts = Arrays.copyOfRange(allClassLoaderContexts,
+ primaryCount, allClassLoaderContexts.length);
+ assertSecondaryUse(mFooUser0, pui, fooSecondaries, /*isUsedByOtherApps*/false, mUser0,
+ secondaryClassLoaderContexts);
+
+ assertHasDclInfo(mFooUser0, mFooUser0, fooSecondaries);
+ }
+
+ @Test
+ public void testNotifySecondary_withSharedLibrary() {
+ // Foo loads its own secondary files.
+ List<String> fooSecondaries = mFooUser0.getSecondaryDexPaths();
+
+ String contextSuffix = "{PCL[/system/framework/org.apache.http.legacy.jar]}";
+ String[] expectedContexts = DexoptUtils.processContextForDexLoad(
+ Arrays.asList(mFooUser0.mClassLoader),
+ Arrays.asList(String.join(File.pathSeparator, fooSecondaries)));
+ for (int i = 0; i < expectedContexts.length; i++) {
+ expectedContexts[i] += contextSuffix;
+ }
+
+ notifyDexLoad(mFooUser0, fooSecondaries, expectedContexts, mUser0);
+
+ PackageUseInfo pui = getPackageUseInfo(mFooUser0);
+ assertIsUsedByOtherApps(mFooUser0, pui, false);
+ assertEquals(fooSecondaries.size(), pui.getDexUseInfoMap().size());
+ assertSecondaryUse(mFooUser0, pui, fooSecondaries, /*isUsedByOtherApps*/false, mUser0,
+ expectedContexts);
+
+ assertHasDclInfo(mFooUser0, mFooUser0, fooSecondaries);
+ }
+
private void assertSecondaryUse(TestData testData, PackageUseInfo pui,
List<String> secondaries, boolean isUsedByOtherApps, int ownerUserId,
String[] expectedContexts) {
@@ -572,17 +627,43 @@
// By default, assume a single class loader in the chain.
// This makes writing tests much easier.
List<String> classLoaders = Arrays.asList(testData.mClassLoader);
- List<String> classPaths = (dexPaths == null)
- ? Arrays.asList((String) null)
- : Arrays.asList(String.join(File.pathSeparator, dexPaths));
+ List<String> classPaths = dexPaths != null
+ ? Arrays.<String>asList(String.join(File.pathSeparator, dexPaths)) : null;
notifyDexLoad(testData, classLoaders, classPaths, loaderUserId);
}
private void notifyDexLoad(TestData testData, List<String> classLoaders,
List<String> classPaths, int loaderUserId) {
+ String[] classLoaderContexts = computeClassLoaderContexts(classLoaders, classPaths);
// We call the internal function so any exceptions thrown cause test failures.
- mDexManager.notifyDexLoadInternal(testData.mPackageInfo.applicationInfo, classLoaders,
- classPaths, testData.mLoaderIsa, loaderUserId);
+ List<String> dexPaths = classPaths != null
+ ? Arrays.asList(classPaths.get(0).split(File.pathSeparator)) : Arrays.asList();
+ notifyDexLoad(testData, dexPaths, classLoaderContexts, loaderUserId);
+ }
+
+ private void notifyDexLoad(TestData testData, List<String> dexPaths,
+ String[] classLoaderContexts, int loaderUserId) {
+ assertTrue(dexPaths.size() == classLoaderContexts.length);
+ HashMap<String, String> dexPathMapping = new HashMap<>(dexPaths.size());
+ for (int i = 0; i < dexPaths.size(); i++) {
+ dexPathMapping.put(dexPaths.get(i), classLoaderContexts != null
+ ? classLoaderContexts[i] : PackageDexUsage.UNSUPPORTED_CLASS_LOADER_CONTEXT);
+ }
+ mDexManager.notifyDexLoadInternal(testData.mPackageInfo.applicationInfo, dexPathMapping,
+ testData.mLoaderIsa, loaderUserId);
+ }
+
+ private String[] computeClassLoaderContexts(List<String> classLoaders,
+ List<String> classPaths) {
+ if (classPaths == null) {
+ return new String[0];
+ }
+ String[] results = DexoptUtils.processContextForDexLoad(classLoaders, classPaths);
+ if (results == null) {
+ results = new String[classPaths.get(0).split(File.pathSeparator).length];
+ Arrays.fill(results, PackageDexUsage.UNSUPPORTED_CLASS_LOADER_CONTEXT);
+ }
+ return results;
}
private PackageUseInfo getPackageUseInfo(TestData testData) {
diff --git a/services/usb/java/com/android/server/usb/UsbDeviceManager.java b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
index 45b840c..121a5b57 100644
--- a/services/usb/java/com/android/server/usb/UsbDeviceManager.java
+++ b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
@@ -41,6 +41,7 @@
import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.debug.AdbManagerInternal;
+import android.debug.AdbTransportType;
import android.debug.IAdbTransport;
import android.hardware.usb.ParcelableUsbPort;
import android.hardware.usb.UsbAccessory;
@@ -774,8 +775,10 @@
}
@Override
- public void onAdbEnabled(boolean enabled) {
- mHandler.sendMessage(MSG_ENABLE_ADB, enabled);
+ public void onAdbEnabled(boolean enabled, byte transportType) {
+ if (transportType == AdbTransportType.USB) {
+ mHandler.sendMessage(MSG_ENABLE_ADB, enabled);
+ }
}
}
@@ -1169,7 +1172,8 @@
}
protected boolean isAdbEnabled() {
- return LocalServices.getService(AdbManagerInternal.class).isAdbEnabled();
+ return LocalServices.getService(AdbManagerInternal.class)
+ .isAdbEnabled(AdbTransportType.USB);
}
protected void updateAdbNotification(boolean force) {
@@ -1714,21 +1718,6 @@
private static final int ENUMERATION_TIME_OUT_MS = 2000;
/**
- * Command to start native service.
- */
- protected static final String CTL_START = "ctl.start";
-
- /**
- * Command to start native service.
- */
- protected static final String CTL_STOP = "ctl.stop";
-
- /**
- * Adb native daemon.
- */
- protected static final String ADBD = "adbd";
-
- /**
* Gadget HAL fully qualified instance name for registering for ServiceNotification.
*/
protected static final String GADGET_HAL_FQ_NAME =
@@ -1913,12 +1902,14 @@
/**
* Start adbd if ADB function is included in the configuration.
*/
- setSystemProperty(CTL_START, ADBD);
+ LocalServices.getService(AdbManagerInternal.class)
+ .startAdbdForTransport(AdbTransportType.USB);
} else {
/**
- * Stop adbd otherwise.
+ * Stop adbd otherwise
*/
- setSystemProperty(CTL_STOP, ADBD);
+ LocalServices.getService(AdbManagerInternal.class)
+ .stopAdbdForTransport(AdbTransportType.USB);
}
UsbGadgetCallback usbGadgetCallback = new UsbGadgetCallback(mCurrentRequest,
config, chargingFunctions);
diff --git a/telecomm/java/android/telecom/Call.java b/telecomm/java/android/telecom/Call.java
old mode 100644
new mode 100755
index 52213d8..c5fcf67
--- a/telecomm/java/android/telecom/Call.java
+++ b/telecomm/java/android/telecom/Call.java
@@ -465,8 +465,27 @@
* @hide
*/
public static final int CAPABILITY_ADD_PARTICIPANT = 0x02000000;
+
+ /**
+ * When set for a call, indicates that this {@code Call} can be transferred to another
+ * number.
+ * Call supports the blind and assured call transfer feature.
+ *
+ * @hide
+ */
+ public static final int CAPABILITY_TRANSFER = 0x04000000;
+
+ /**
+ * When set for a call, indicates that this {@code Call} can be transferred to another
+ * ongoing call.
+ * Call supports the consultative call transfer feature.
+ *
+ * @hide
+ */
+ public static final int CAPABILITY_TRANSFER_CONSULTATIVE = 0x08000000;
+
//******************************************************************************************
- // Next CAPABILITY value: 0x04000000
+ // Next CAPABILITY value: 0x10000000
//******************************************************************************************
/**
@@ -699,6 +718,12 @@
if (can(capabilities, CAPABILITY_ADD_PARTICIPANT)) {
builder.append(" CAPABILITY_ADD_PARTICIPANT");
}
+ if (can(capabilities, CAPABILITY_TRANSFER)) {
+ builder.append(" CAPABILITY_TRANSFER");
+ }
+ if (can(capabilities, CAPABILITY_TRANSFER_CONSULTATIVE)) {
+ builder.append(" CAPABILITY_TRANSFER_CONSULTATIVE");
+ }
builder.append("]");
return builder.toString();
}
@@ -1564,6 +1589,30 @@
}
/**
+ * Instructs this {@code Call} to be transferred to another number.
+ *
+ * @param targetNumber The address to which the call will be transferred.
+ * @param isConfirmationRequired if {@code true} it will initiate ASSURED transfer,
+ * if {@code false}, it will initiate BLIND transfer.
+ *
+ * @hide
+ */
+ public void transfer(@NonNull Uri targetNumber, boolean isConfirmationRequired) {
+ mInCallAdapter.transferCall(mTelecomCallId, targetNumber, isConfirmationRequired);
+ }
+
+ /**
+ * Instructs this {@code Call} to be transferred to another ongoing call.
+ * This will initiate CONSULTATIVE transfer.
+ * @param toCall The other ongoing {@code Call} to which this call will be transferred.
+ *
+ * @hide
+ */
+ public void transfer(@NonNull android.telecom.Call toCall) {
+ mInCallAdapter.transferCall(mTelecomCallId, toCall.mTelecomCallId);
+ }
+
+ /**
* Instructs this {@code Call} to disconnect.
*/
public void disconnect() {
diff --git a/telecomm/java/android/telecom/Connection.java b/telecomm/java/android/telecom/Connection.java
old mode 100644
new mode 100755
index 3b0ba25..5f33a3d
--- a/telecomm/java/android/telecom/Connection.java
+++ b/telecomm/java/android/telecom/Connection.java
@@ -387,8 +387,25 @@
* @hide
*/
public static final int CAPABILITY_ADD_PARTICIPANT = 0x04000000;
+
+ /**
+ * Indicates that this {@code Connection} can be transferred to another
+ * number.
+ * Connection supports the blind and assured call transfer feature.
+ * @hide
+ */
+ public static final int CAPABILITY_TRANSFER = 0x08000000;
+
+ /**
+ * Indicates that this {@code Connection} can be transferred to another
+ * ongoing {@code Connection}.
+ * Connection supports the consultative call transfer feature.
+ * @hide
+ */
+ public static final int CAPABILITY_TRANSFER_CONSULTATIVE = 0x10000000;
+
//**********************************************************************************************
- // Next CAPABILITY value: 0x08000000
+ // Next CAPABILITY value: 0x20000000
//**********************************************************************************************
/**
@@ -967,6 +984,13 @@
if ((capabilities & CAPABILITY_ADD_PARTICIPANT) == CAPABILITY_ADD_PARTICIPANT) {
builder.append(isLong ? " CAPABILITY_ADD_PARTICIPANT" : " add_participant");
}
+ if ((capabilities & CAPABILITY_TRANSFER) == CAPABILITY_TRANSFER) {
+ builder.append(isLong ? " CAPABILITY_TRANSFER" : " sup_trans");
+ }
+ if ((capabilities & CAPABILITY_TRANSFER_CONSULTATIVE)
+ == CAPABILITY_TRANSFER_CONSULTATIVE) {
+ builder.append(isLong ? " CAPABILITY_TRANSFER_CONSULTATIVE" : " sup_cTrans");
+ }
builder.append("]");
return builder.toString();
}
@@ -3092,6 +3116,26 @@
public void onReject(String replyMessage) {}
/**
+ * Notifies this Connection, a request to transfer to a target number.
+ * @param number the number to transfer this {@link Connection} to.
+ * @param isConfirmationRequired when {@code true}, the {@link ConnectionService}
+ * should wait until the transfer has successfully completed before disconnecting
+ * the current {@link Connection}.
+ * When {@code false}, the {@link ConnectionService} should signal the network to
+ * perform the transfer, but should immediately disconnect the call regardless of
+ * the outcome of the transfer.
+ * @hide
+ */
+ public void onTransfer(@NonNull Uri number, boolean isConfirmationRequired) {}
+
+ /**
+ * Notifies this Connection, a request to transfer to another Connection.
+ * @param otherConnection the {@link Connection} to transfer this call to.
+ * @hide
+ */
+ public void onTransfer(@NonNull Connection otherConnection) {}
+
+ /**
* Notifies this Connection of a request to silence the ringer.
* <p>
* The ringer may be silenced by any of the following methods:
@@ -3314,7 +3358,6 @@
private boolean mImmutable = false;
public FailureSignalingConnection(DisconnectCause disconnectCause) {
setDisconnected(disconnectCause);
- mImmutable = true;
}
public void checkImmutable() {
@@ -3532,7 +3575,7 @@
* ATIS-1000082.
* @return the verification status.
*/
- public @VerificationStatus int getCallerNumberVerificationStatus() {
+ public final @VerificationStatus int getCallerNumberVerificationStatus() {
return mCallerNumberVerificationStatus;
}
@@ -3544,7 +3587,7 @@
* by
* {@link ConnectionService#onCreateIncomingConnection(PhoneAccountHandle, ConnectionRequest)}.
*/
- public void setCallerNumberVerificationStatus(
+ public final void setCallerNumberVerificationStatus(
@VerificationStatus int callerNumberVerificationStatus) {
mCallerNumberVerificationStatus = callerNumberVerificationStatus;
}
diff --git a/telecomm/java/android/telecom/ConnectionService.java b/telecomm/java/android/telecom/ConnectionService.java
old mode 100644
new mode 100755
index 2aea723..0dca006
--- a/telecomm/java/android/telecom/ConnectionService.java
+++ b/telecomm/java/android/telecom/ConnectionService.java
@@ -128,6 +128,8 @@
private static final String SESSION_ANSWER = "CS.an";
private static final String SESSION_ANSWER_VIDEO = "CS.anV";
private static final String SESSION_DEFLECT = "CS.def";
+ private static final String SESSION_TRANSFER = "CS.trans";
+ private static final String SESSION_CONSULTATIVE_TRANSFER = "CS.cTrans";
private static final String SESSION_REJECT = "CS.r";
private static final String SESSION_REJECT_MESSAGE = "CS.rWM";
private static final String SESSION_SILENCE = "CS.s";
@@ -196,6 +198,8 @@
private static final int MSG_CREATE_CONFERENCE_FAILED = 37;
private static final int MSG_REJECT_WITH_REASON = 38;
private static final int MSG_ADD_PARTICIPANT = 39;
+ private static final int MSG_EXPLICIT_CALL_TRANSFER = 40;
+ private static final int MSG_EXPLICIT_CALL_TRANSFER_CONSULTATIVE = 41;
private static Connection sNullConnection;
@@ -481,6 +485,38 @@
}
@Override
+ public void transfer(@NonNull String callId, @NonNull Uri number,
+ boolean isConfirmationRequired, Session.Info sessionInfo) {
+ Log.startSession(sessionInfo, SESSION_TRANSFER);
+ try {
+ SomeArgs args = SomeArgs.obtain();
+ args.arg1 = callId;
+ args.arg2 = number;
+ args.argi1 = isConfirmationRequired ? 1 : 0;
+ args.arg3 = Log.createSubsession();
+ mHandler.obtainMessage(MSG_EXPLICIT_CALL_TRANSFER, args).sendToTarget();
+ } finally {
+ Log.endSession();
+ }
+ }
+
+ @Override
+ public void consultativeTransfer(@NonNull String callId, @NonNull String otherCallId,
+ Session.Info sessionInfo) {
+ Log.startSession(sessionInfo, SESSION_CONSULTATIVE_TRANSFER);
+ try {
+ SomeArgs args = SomeArgs.obtain();
+ args.arg1 = callId;
+ args.arg2 = otherCallId;
+ args.arg3 = Log.createSubsession();
+ mHandler.obtainMessage(
+ MSG_EXPLICIT_CALL_TRANSFER_CONSULTATIVE, args).sendToTarget();
+ } finally {
+ Log.endSession();
+ }
+ }
+
+ @Override
public void silence(String callId, Session.Info sessionInfo) {
Log.startSession(sessionInfo, SESSION_SILENCE);
try {
@@ -1108,6 +1144,30 @@
}
break;
}
+ case MSG_EXPLICIT_CALL_TRANSFER: {
+ SomeArgs args = (SomeArgs) msg.obj;
+ Log.continueSession((Session) args.arg3, SESSION_HANDLER + SESSION_TRANSFER);
+ try {
+ final boolean isConfirmationRequired = args.argi1 == 1;
+ transfer((String) args.arg1, (Uri) args.arg2, isConfirmationRequired);
+ } finally {
+ args.recycle();
+ Log.endSession();
+ }
+ break;
+ }
+ case MSG_EXPLICIT_CALL_TRANSFER_CONSULTATIVE: {
+ SomeArgs args = (SomeArgs) msg.obj;
+ Log.continueSession(
+ (Session) args.arg3, SESSION_HANDLER + SESSION_CONSULTATIVE_TRANSFER);
+ try {
+ consultativeTransfer((String) args.arg1, (String) args.arg2);
+ } finally {
+ args.recycle();
+ Log.endSession();
+ }
+ break;
+ }
case MSG_DISCONNECT: {
SomeArgs args = (SomeArgs) msg.obj;
Log.continueSession((Session) args.arg2, SESSION_HANDLER + SESSION_DISCONNECT);
@@ -2042,6 +2102,18 @@
findConnectionForAction(callId, "reject").onReject(rejectReason);
}
+ private void transfer(String callId, Uri number, boolean isConfirmationRequired) {
+ Log.d(this, "transfer %s", callId);
+ findConnectionForAction(callId, "transfer").onTransfer(number, isConfirmationRequired);
+ }
+
+ private void consultativeTransfer(String callId, String otherCallId) {
+ Log.d(this, "consultativeTransfer %s", callId);
+ Connection connection1 = findConnectionForAction(callId, "consultativeTransfer");
+ Connection connection2 = findConnectionForAction(otherCallId, " consultativeTransfer");
+ connection1.onTransfer(connection2);
+ }
+
private void silence(String callId) {
Log.d(this, "silence %s", callId);
findConnectionForAction(callId, "silence").onSilence();
diff --git a/telecomm/java/android/telecom/InCallAdapter.java b/telecomm/java/android/telecom/InCallAdapter.java
old mode 100644
new mode 100755
index 9d291740..dd6c153
--- a/telecomm/java/android/telecom/InCallAdapter.java
+++ b/telecomm/java/android/telecom/InCallAdapter.java
@@ -16,6 +16,7 @@
package android.telecom;
+import android.annotation.NonNull;
import android.bluetooth.BluetoothDevice;
import android.net.Uri;
import android.os.Bundle;
@@ -102,6 +103,35 @@
}
/**
+ * Instructs Telecom to transfer the specified call.
+ *
+ * @param callId The identifier of the call to transfer.
+ * @param targetNumber The address to transfer to.
+ * @param isConfirmationRequired if {@code true} it will initiate ASSURED transfer,
+ * if {@code false}, it will initiate BLIND transfer.
+ */
+ public void transferCall(@NonNull String callId, @NonNull Uri targetNumber,
+ boolean isConfirmationRequired) {
+ try {
+ mAdapter.transferCall(callId, targetNumber, isConfirmationRequired);
+ } catch (RemoteException e) {
+ }
+ }
+
+ /**
+ * Instructs Telecom to transfer the specified call to another ongoing call.
+ *
+ * @param callId The identifier of the call to transfer.
+ * @param otherCallId The identifier of the other call to which this will be transferred.
+ */
+ public void transferCall(@NonNull String callId, @NonNull String otherCallId) {
+ try {
+ mAdapter.consultativeTransfer(callId, otherCallId);
+ } catch (RemoteException e) {
+ }
+ }
+
+ /**
* Instructs Telecom to disconnect the specified call.
*
* @param callId The identifier of the call to disconnect.
diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java
index a7024f9..19a1021e 100644
--- a/telecomm/java/android/telecom/TelecomManager.java
+++ b/telecomm/java/android/telecom/TelecomManager.java
@@ -326,6 +326,14 @@
"android.telecom.extra.CALL_CREATED_TIME_MILLIS";
/**
+ * Optional extra for incoming and outgoing calls containing a long which specifies the Epoch
+ * time the call was created.
+ * @hide
+ */
+ public static final String EXTRA_CALL_CREATED_EPOCH_TIME_MILLIS =
+ "android.telecom.extra.CALL_CREATED_EPOCH_TIME_MILLIS";
+
+ /**
* Optional extra for incoming and outgoing calls containing a long which specifies the time
* telecom began routing the call. This value is in milliseconds since boot.
* @hide
diff --git a/telecomm/java/com/android/internal/telecom/IConnectionService.aidl b/telecomm/java/com/android/internal/telecom/IConnectionService.aidl
index a397d77..fb54179 100644
--- a/telecomm/java/com/android/internal/telecom/IConnectionService.aidl
+++ b/telecomm/java/com/android/internal/telecom/IConnectionService.aidl
@@ -81,6 +81,11 @@
void rejectWithMessage(String callId, String message, in Session.Info sessionInfo);
+ void transfer(String callId, in Uri number, boolean isConfirmationRequired,
+ in Session.Info sessionInfo);
+
+ void consultativeTransfer(String callId, String otherCallId, in Session.Info sessionInfo);
+
void disconnect(String callId, in Session.Info sessionInfo);
void silence(String callId, in Session.Info sessionInfo);
diff --git a/telecomm/java/com/android/internal/telecom/IInCallAdapter.aidl b/telecomm/java/com/android/internal/telecom/IInCallAdapter.aidl
old mode 100644
new mode 100755
index 9beff22..edf1cf4
--- a/telecomm/java/com/android/internal/telecom/IInCallAdapter.aidl
+++ b/telecomm/java/com/android/internal/telecom/IInCallAdapter.aidl
@@ -36,6 +36,10 @@
void rejectCallWithReason(String callId, int rejectReason);
+ void transferCall(String callId, in Uri targetNumber, boolean isConfirmationRequired);
+
+ void consultativeTransfer(String callId, String otherCallId);
+
void disconnectCall(String callId);
void holdCall(String callId);
diff --git a/telephony/common/com/google/android/mms/util/DrmConvertSession.java b/telephony/common/com/google/android/mms/util/DrmConvertSession.java
index 156c7ad..17ab1547 100644
--- a/telephony/common/com/google/android/mms/util/DrmConvertSession.java
+++ b/telephony/common/com/google/android/mms/util/DrmConvertSession.java
@@ -20,7 +20,6 @@
import android.content.Context;
import android.drm.DrmConvertedStatus;
import android.drm.DrmManagerClient;
-import android.provider.Downloads;
import android.util.Log;
import java.io.FileNotFoundException;
@@ -33,6 +32,13 @@
private int mConvertSessionId;
private static final String TAG = "DrmConvertSession";
+ // These values are copied from Downloads.Impl.* for backward compatibility since
+ // {@link #close()} that uses it is marked @UnsupportedAppUsage.
+ public static final int STATUS_SUCCESS = 200;
+ public static final int STATUS_NOT_ACCEPTABLE = 406;
+ public static final int STATUS_UNKNOWN_ERROR = 491;
+ public static final int STATUS_FILE_ERROR = 492;
+
private DrmConvertSession(DrmManagerClient drmClient, int convertSessionId) {
mDrmClient = drmClient;
mConvertSessionId = convertSessionId;
@@ -118,38 +124,38 @@
* Ends a conversion session of a file.
*
* @param fileName The filename of the converted file.
- * @return Downloads.Impl.STATUS_SUCCESS if execution is ok.
- * Downloads.Impl.STATUS_FILE_ERROR in case converted file can not
- * be accessed. Downloads.Impl.STATUS_NOT_ACCEPTABLE if a problem
+ * @return STATUS_SUCCESS if execution is ok.
+ * STATUS_FILE_ERROR in case converted file can not
+ * be accessed. STATUS_NOT_ACCEPTABLE if a problem
* occurs when accessing drm framework.
- * Downloads.Impl.STATUS_UNKNOWN_ERROR if a general error occurred.
+ * STATUS_UNKNOWN_ERROR if a general error occurred.
*/
@UnsupportedAppUsage
public int close(String filename) {
DrmConvertedStatus convertedStatus = null;
- int result = Downloads.Impl.STATUS_UNKNOWN_ERROR;
+ int result = STATUS_UNKNOWN_ERROR;
if (mDrmClient != null && mConvertSessionId >= 0) {
try {
convertedStatus = mDrmClient.closeConvertSession(mConvertSessionId);
if (convertedStatus == null ||
convertedStatus.statusCode != DrmConvertedStatus.STATUS_OK ||
convertedStatus.convertedData == null) {
- result = Downloads.Impl.STATUS_NOT_ACCEPTABLE;
+ result = STATUS_NOT_ACCEPTABLE;
} else {
RandomAccessFile rndAccessFile = null;
try {
rndAccessFile = new RandomAccessFile(filename, "rw");
rndAccessFile.seek(convertedStatus.offset);
rndAccessFile.write(convertedStatus.convertedData);
- result = Downloads.Impl.STATUS_SUCCESS;
+ result = STATUS_SUCCESS;
} catch (FileNotFoundException e) {
- result = Downloads.Impl.STATUS_FILE_ERROR;
+ result = STATUS_FILE_ERROR;
Log.w(TAG, "File: " + filename + " could not be found.", e);
} catch (IOException e) {
- result = Downloads.Impl.STATUS_FILE_ERROR;
+ result = STATUS_FILE_ERROR;
Log.w(TAG, "Could not access File: " + filename + " .", e);
} catch (IllegalArgumentException e) {
- result = Downloads.Impl.STATUS_FILE_ERROR;
+ result = STATUS_FILE_ERROR;
Log.w(TAG, "Could not open file in mode: rw", e);
} catch (SecurityException e) {
Log.w(TAG, "Access to File: " + filename +
@@ -159,7 +165,7 @@
try {
rndAccessFile.close();
} catch (IOException e) {
- result = Downloads.Impl.STATUS_FILE_ERROR;
+ result = STATUS_FILE_ERROR;
Log.w(TAG, "Failed to close File:" + filename
+ ".", e);
}
diff --git a/core/java/android/service/euicc/IEuiccServiceDumpResultCallback.aidl b/telephony/java/android/service/euicc/IEuiccServiceDumpResultCallback.aidl
similarity index 100%
rename from core/java/android/service/euicc/IEuiccServiceDumpResultCallback.aidl
rename to telephony/java/android/service/euicc/IEuiccServiceDumpResultCallback.aidl
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 233eced..8cf113f 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -319,6 +319,34 @@
"only_auto_select_in_home_network";
/**
+ * Flag indicating whether to show single operator row in the choose network setting.
+ *
+ * The device configuration value {@code config_enableNewAutoSelectNetworkUI} ultimately
+ * controls whether this carrier configuration option is used. Where
+ * {@code config_enableNewAutoSelectNetworkUI} is false, the value of the
+ * {@link #KEY_SHOW_SINGLE_OPERATOR_ROW_IN_CHOOSE_NETWORK_SETTING_BOOL} carrier configuration
+ * option is ignored.
+ *
+ * If {@code true}, default value, merge the duplicate networks which with the same plmn, keep
+ * the one that with the higher signal strength level.
+ * If {@code false}, show all operators without merging.
+ * @hide
+ */
+ public static final String KEY_SHOW_SINGLE_OPERATOR_ROW_IN_CHOOSE_NETWORK_SETTING_BOOL =
+ "show_single_operator_row_in_choose_network_setting_bool";
+
+ /**
+ * Flag indicating whether to display SPN as network name for home network in choose
+ * network setting.
+ *
+ * If {@code true}, display SPN as network name in choose network setting.
+ * If {@code false}, display PLMN in choose network setting.
+ * @hide
+ */
+ public static final String KEY_SHOW_SPN_FOR_HOME_IN_CHOOSE_NETWORK_SETTING_BOOL =
+ "show_spn_for_home_in_choose_network_setting_bool";
+
+ /**
* Control whether users receive a simplified network settings UI and improved network
* selection.
*/
@@ -839,7 +867,8 @@
/**
* The default flag specifying whether ETWS/CMAS test setting is forcibly disabled in
* Settings->More->Emergency broadcasts menu even though developer options is turned on.
- * @deprecated moved to cellbroadcastreceiver resource show_test_settings
+ * @deprecated Use {@code com.android.cellbroadcastreceiver.CellBroadcastReceiver} resource
+ * {@code show_test_settings} to control whether to show test alert settings or not.
*/
@Deprecated
public static final String KEY_CARRIER_FORCE_DISABLE_ETWS_CMAS_TEST_BOOL =
@@ -1936,6 +1965,13 @@
"carrier_allow_deflect_ims_call_bool";
/**
+ * Flag indicating whether the carrier supports explicit call transfer for an IMS call.
+ * @hide
+ */
+ public static final String KEY_CARRIER_ALLOW_TRANSFER_IMS_CALL_BOOL =
+ "carrier_allow_transfer_ims_call_bool";
+
+ /**
* Flag indicating whether the carrier always wants to play an "on-hold" tone when a call has
* been remotely held.
* <p>
@@ -1965,10 +2001,15 @@
"allow_add_call_during_video_call";
/**
- * When false, indicates that holding a video call is disabled
+ * When {@code true}, indicates that video calls can be put on hold in order to swap to another
+ * call (e.g. a new outgoing call).
+ * When {@code false}, indicates that video calls will be disconnected when swapping to another
+ * call.
+ * <p>
+ * This is {@code true} by default.
*/
- public static final String KEY_ALLOW_HOLDING_VIDEO_CALL_BOOL =
- "allow_holding_video_call";
+ public static final String KEY_ALLOW_HOLD_VIDEO_CALL_BOOL =
+ "allow_hold_video_call_bool";
/**
* When true, indicates that the HD audio icon in the in-call screen should not be shown for
@@ -3336,6 +3377,25 @@
"subscription_group_uuid_string";
/**
+ * Data switch validation minimal gap time, in milliseconds.
+ *
+ * Which means, if the same subscription on the same network (based on MCC+MNC+TAC+subId)
+ * was recently validated (within this time gap), and Telephony receives a request to switch to
+ * it again, Telephony will skip the validation part and switch to it as soon as connection
+ * is setup, as if it's already validated.
+ *
+ * If the network was validated within the gap but the latest validation result is false, the
+ * validation will not be skipped.
+ *
+ * If not set or set to 0, validation will never be skipped.
+ * The max acceptable value of this config is 24 hours.
+ *
+ * @hide
+ */
+ public static final String KEY_DATA_SWITCH_VALIDATION_MIN_GAP_LONG =
+ "data_switch_validation_min_gap_LONG";
+
+ /**
* A boolean property indicating whether this subscription should be managed as an opportunistic
* subscription.
*
@@ -3398,6 +3458,15 @@
"carrier_certificate_string_array";
/**
+ * Flag specifying whether the incoming call number should be formatted to national number
+ * for Japan. @return {@code true} convert to the national format, {@code false} otherwise.
+ * e.g. "+819012345678" -> "09012345678"
+ * @hide
+ */
+ public static final String KEY_FORMAT_INCOMING_NUMBER_TO_NATIONAL_FOR_JP_BOOL =
+ "format_incoming_number_to_national_for_jp_bool";
+
+ /**
* DisconnectCause array to play busy tone. Value should be array of
* {@link android.telephony.DisconnectCause}.
*/
@@ -3412,6 +3481,30 @@
public static final String KEY_PREVENT_CLIR_ACTIVATION_AND_DEACTIVATION_CODE_BOOL =
"prevent_clir_activation_and_deactivation_code_bool";
+ /**
+ * The list of originating address of missed incoming call SMS. If the SMS has originator
+ * matched, the SMS will be treated as special SMS for notifying missed incoming call to the
+ * user.
+ *
+ * @hide
+ */
+ public static final String KEY_MISSED_INCOMING_CALL_SMS_ORIGINATOR_STRING_ARRAY =
+ "missed_incoming_call_sms_originator_string_array";
+
+ /**
+ * The patterns of missed incoming call sms. This is the regular expression used for
+ * matching the missed incoming call's date, time, and caller id. The pattern should match
+ * fields for at least month, day, hour, and minute. Year is optional although it is encouraged.
+ *
+ * An usable pattern should look like this:
+ * ^(?<month>0[1-9]|1[012])\/(?<day>0[1-9]|1[0-9]|2[0-9]|3[0-1]) (?<hour>[0-1][0-9]|2[0-3]):
+ * (?<minute>[0-5][0-9])\s*(?<callerId>[0-9]+)\s*$
+ *
+ * @hide
+ */
+ public static final String KEY_MISSED_INCOMING_CALL_SMS_PATTERN_STRING_ARRAY =
+ "missed_incoming_call_sms_pattern_string_array";
+
/** The default value for every variable. */
private final static PersistableBundle sDefaults;
@@ -3420,6 +3513,7 @@
sDefaults.putString(KEY_CARRIER_CONFIG_VERSION_STRING, "");
sDefaults.putBoolean(KEY_ALLOW_HOLD_IN_IMS_CALL_BOOL, true);
sDefaults.putBoolean(KEY_CARRIER_ALLOW_DEFLECT_IMS_CALL_BOOL, false);
+ sDefaults.putBoolean(KEY_CARRIER_ALLOW_TRANSFER_IMS_CALL_BOOL, false);
sDefaults.putBoolean(KEY_ALWAYS_PLAY_REMOTE_HOLD_TONE_BOOL, false);
sDefaults.putBoolean(KEY_AUTO_RETRY_FAILED_WIFI_EMERGENCY_CALL, false);
sDefaults.putBoolean(KEY_ADDITIONAL_CALL_SETTING_BOOL, true);
@@ -3475,6 +3569,8 @@
sDefaults.putBoolean(KEY_HAS_IN_CALL_NOISE_SUPPRESSION_BOOL, false);
sDefaults.putBoolean(KEY_HIDE_CARRIER_NETWORK_SETTINGS_BOOL, false);
sDefaults.putBoolean(KEY_ONLY_AUTO_SELECT_IN_HOME_NETWORK_BOOL, false);
+ sDefaults.putBoolean(KEY_SHOW_SINGLE_OPERATOR_ROW_IN_CHOOSE_NETWORK_SETTING_BOOL, true);
+ sDefaults.putBoolean(KEY_SHOW_SPN_FOR_HOME_IN_CHOOSE_NETWORK_SETTING_BOOL, false);
sDefaults.putBoolean(KEY_SIMPLIFIED_NETWORK_SETTINGS_BOOL, false);
sDefaults.putBoolean(KEY_HIDE_SIM_LOCK_SETTINGS_BOOL, false);
@@ -3713,7 +3809,7 @@
sDefaults.putBoolean(KEY_DROP_VIDEO_CALL_WHEN_ANSWERING_AUDIO_CALL_BOOL, false);
sDefaults.putBoolean(KEY_ALLOW_MERGE_WIFI_CALLS_WHEN_VOWIFI_OFF_BOOL, true);
sDefaults.putBoolean(KEY_ALLOW_ADD_CALL_DURING_VIDEO_CALL_BOOL, true);
- sDefaults.putBoolean(KEY_ALLOW_HOLDING_VIDEO_CALL_BOOL, true);
+ sDefaults.putBoolean(KEY_ALLOW_HOLD_VIDEO_CALL_BOOL, true);
sDefaults.putBoolean(KEY_WIFI_CALLS_CAN_BE_HD_AUDIO, true);
sDefaults.putBoolean(KEY_VIDEO_CALLS_CAN_BE_HD_AUDIO, true);
sDefaults.putBoolean(KEY_GSM_CDMA_CALLS_CAN_BE_HD_AUDIO, false);
@@ -3896,12 +3992,17 @@
sDefaults.putBoolean(KEY_SUPPORT_WPS_OVER_IMS_BOOL, true);
sDefaults.putAll(Ims.getDefaults());
sDefaults.putStringArray(KEY_CARRIER_CERTIFICATE_STRING_ARRAY, null);
+ sDefaults.putBoolean(KEY_FORMAT_INCOMING_NUMBER_TO_NATIONAL_FOR_JP_BOOL, false);
sDefaults.putIntArray(KEY_DISCONNECT_CAUSE_PLAY_BUSYTONE_INT_ARRAY,
new int[] {4 /* BUSY */});
sDefaults.putBoolean(KEY_PREVENT_CLIR_ACTIVATION_AND_DEACTIVATION_CODE_BOOL, false);
sDefaults.putLong(KEY_DATA_SWITCH_VALIDATION_TIMEOUT_LONG, 2000);
sDefaults.putInt(KEY_PARAMETERS_USED_FOR_LTE_SIGNAL_BAR_INT,
CellSignalStrengthLte.USE_RSRP);
+ sDefaults.putLong(KEY_DATA_SWITCH_VALIDATION_MIN_GAP_LONG, 0);
+ sDefaults.putStringArray(KEY_MISSED_INCOMING_CALL_SMS_ORIGINATOR_STRING_ARRAY,
+ new String[0]);
+ sDefaults.putStringArray(KEY_MISSED_INCOMING_CALL_SMS_PATTERN_STRING_ARRAY, new String[0]);
}
/**
@@ -4120,6 +4221,7 @@
} catch (RemoteException ex) {
Rlog.e(TAG, "getDefaultCarrierServicePackageName ICarrierConfigLoader is null"
+ ex.toString());
+ ex.rethrowAsRuntimeException();
}
return "";
}
diff --git a/telephony/java/android/telephony/CellSignalStrengthLte.java b/telephony/java/android/telephony/CellSignalStrengthLte.java
index 4978dc1..2529387 100644
--- a/telephony/java/android/telephony/CellSignalStrengthLte.java
+++ b/telephony/java/android/telephony/CellSignalStrengthLte.java
@@ -244,19 +244,30 @@
} else {
mParametersUseForLevel = cc.getInt(
CarrierConfigManager.KEY_PARAMETERS_USED_FOR_LTE_SIGNAL_BAR_INT);
- Rlog.i(LOG_TAG, "Using signal strength level: " + mParametersUseForLevel);
+ if (DBG) {
+ Rlog.i(LOG_TAG, "Using signal strength level: " + mParametersUseForLevel);
+ }
rsrpThresholds = cc.getIntArray(
CarrierConfigManager.KEY_LTE_RSRP_THRESHOLDS_INT_ARRAY);
if (rsrpThresholds == null) rsrpThresholds = sRsrpThresholds;
- Rlog.i(LOG_TAG, "Applying LTE RSRP Thresholds: " + Arrays.toString(rsrpThresholds));
+ if (DBG) {
+ Rlog.i(LOG_TAG, "Applying LTE RSRP Thresholds: "
+ + Arrays.toString(rsrpThresholds));
+ }
rsrqThresholds = cc.getIntArray(
CarrierConfigManager.KEY_LTE_RSRQ_THRESHOLDS_INT_ARRAY);
if (rsrqThresholds == null) rsrqThresholds = sRsrqThresholds;
- Rlog.i(LOG_TAG, "Applying LTE RSRQ Thresholds: " + Arrays.toString(rsrqThresholds));
+ if (DBG) {
+ Rlog.i(LOG_TAG, "Applying LTE RSRQ Thresholds: "
+ + Arrays.toString(rsrqThresholds));
+ }
rssnrThresholds = cc.getIntArray(
CarrierConfigManager.KEY_LTE_RSSNR_THRESHOLDS_INT_ARRAY);
if (rssnrThresholds == null) rssnrThresholds = sRssnrThresholds;
- Rlog.i(LOG_TAG, "Applying LTE RSSNR Thresholds: " + Arrays.toString(rssnrThresholds));
+ if (DBG) {
+ Rlog.i(LOG_TAG, "Applying LTE RSSNR Thresholds: "
+ + Arrays.toString(rssnrThresholds));
+ }
rsrpOnly = cc.getBoolean(
CarrierConfigManager.KEY_USE_ONLY_RSRP_FOR_LTE_SIGNAL_BAR_BOOL, false);
}
@@ -283,15 +294,21 @@
if (isLevelForParameter(USE_RSRP)) {
rsrpLevel = updateLevelWithMeasure(rsrp, rsrpThresholds);
- Rlog.i(LOG_TAG, "Updated 4G LTE RSRP Level: " + rsrpLevel);
+ if (DBG) {
+ Rlog.i(LOG_TAG, "Updated 4G LTE RSRP Level: " + rsrpLevel);
+ }
}
if (isLevelForParameter(USE_RSRQ)) {
rsrqLevel = updateLevelWithMeasure(mRsrq, rsrqThresholds);
- Rlog.i(LOG_TAG, "Updated 4G LTE RSRQ Level: " + rsrqLevel);
+ if (DBG) {
+ Rlog.i(LOG_TAG, "Updated 4G LTE RSRQ Level: " + rsrqLevel);
+ }
}
if (isLevelForParameter(USE_RSSNR)) {
rssnrLevel = updateLevelWithMeasure(mRssnr, rssnrThresholds);
- Rlog.i(LOG_TAG, "Updated 4G LTE RSSNR Level: " + rssnrLevel);
+ if (DBG) {
+ Rlog.i(LOG_TAG, "Updated 4G LTE RSSNR Level: " + rssnrLevel);
+ }
}
// Apply the smaller value among three levels of three measures.
mLevel = Math.min(Math.min(rsrpLevel, rsrqLevel), rssnrLevel);
diff --git a/telephony/java/android/telephony/CellSignalStrengthNr.java b/telephony/java/android/telephony/CellSignalStrengthNr.java
index e3d03a3..8562df1 100644
--- a/telephony/java/android/telephony/CellSignalStrengthNr.java
+++ b/telephony/java/android/telephony/CellSignalStrengthNr.java
@@ -40,6 +40,8 @@
*/
public static final int UNKNOWN_ASU_LEVEL = 99;
+ private static final boolean VDBG = false;
+
private static final String TAG = "CellSignalStrengthNr";
// Lifted from Default carrier configs and max range of SSRSRP
@@ -301,31 +303,45 @@
} else {
mParametersUseForLevel = cc.getInt(
CarrierConfigManager.KEY_PARAMETERS_USE_FOR_5G_NR_SIGNAL_BAR_INT, USE_SSRSRP);
- Rlog.i(TAG, "Using SSRSRP for Level.");
mSsRsrpThresholds = cc.getIntArray(
CarrierConfigManager.KEY_5G_NR_SSRSRP_THRESHOLDS_INT_ARRAY);
- Rlog.i(TAG, "Applying 5G NR SSRSRP Thresholds: " + Arrays.toString(mSsRsrpThresholds));
+ if (VDBG) {
+ Rlog.i(TAG, "Applying 5G NR SSRSRP Thresholds: "
+ + Arrays.toString(mSsRsrpThresholds));
+ }
mSsRsrqThresholds = cc.getIntArray(
CarrierConfigManager.KEY_5G_NR_SSRSRQ_THRESHOLDS_INT_ARRAY);
- Rlog.i(TAG, "Applying 5G NR SSRSRQ Thresholds: " + Arrays.toString(mSsRsrqThresholds));
+ if (VDBG) {
+ Rlog.i(TAG, "Applying 5G NR SSRSRQ Thresholds: "
+ + Arrays.toString(mSsRsrqThresholds));
+ }
mSsSinrThresholds = cc.getIntArray(
CarrierConfigManager.KEY_5G_NR_SSSINR_THRESHOLDS_INT_ARRAY);
- Rlog.i(TAG, "Applying 5G NR SSSINR Thresholds: " + Arrays.toString(mSsSinrThresholds));
+ if (VDBG) {
+ Rlog.i(TAG, "Applying 5G NR SSSINR Thresholds: "
+ + Arrays.toString(mSsSinrThresholds));
+ }
}
int ssRsrpLevel = SignalStrength.INVALID;
int ssRsrqLevel = SignalStrength.INVALID;
int ssSinrLevel = SignalStrength.INVALID;
if (isLevelForParameter(USE_SSRSRP)) {
ssRsrpLevel = updateLevelWithMeasure(mSsRsrp, mSsRsrpThresholds);
- Rlog.i(TAG, "Updated 5G NR SSRSRP Level: " + ssRsrpLevel);
+ if (VDBG) {
+ Rlog.i(TAG, "Updated 5G NR SSRSRP Level: " + ssRsrpLevel);
+ }
}
if (isLevelForParameter(USE_SSRSRQ)) {
ssRsrqLevel = updateLevelWithMeasure(mSsRsrq, mSsRsrqThresholds);
- Rlog.i(TAG, "Updated 5G NR SSRSRQ Level: " + ssRsrqLevel);
+ if (VDBG) {
+ Rlog.i(TAG, "Updated 5G NR SSRSRQ Level: " + ssRsrqLevel);
+ }
}
if (isLevelForParameter(USE_SSSINR)) {
ssSinrLevel = updateLevelWithMeasure(mSsSinr, mSsSinrThresholds);
- Rlog.i(TAG, "Updated 5G NR SSSINR Level: " + ssSinrLevel);
+ if (VDBG) {
+ Rlog.i(TAG, "Updated 5G NR SSSINR Level: " + ssSinrLevel);
+ }
}
// Apply the smaller value among three levels of three measures.
mLevel = Math.min(Math.min(ssRsrpLevel, ssRsrqLevel), ssSinrLevel);
diff --git a/telephony/java/android/telephony/ModemActivityInfo.java b/telephony/java/android/telephony/ModemActivityInfo.java
index bd2375f..6f92406 100644
--- a/telephony/java/android/telephony/ModemActivityInfo.java
+++ b/telephony/java/android/telephony/ModemActivityInfo.java
@@ -199,7 +199,7 @@
}
/**
- * Indicate if the ModemActivityInfo is invalid due to modem's invalid reporting.
+ * Indicates if the modem has reported valid {@link ModemActivityInfo}.
*
* @return {@code true} if this {@link ModemActivityInfo} record is valid,
* {@code false} otherwise.
diff --git a/telephony/java/android/telephony/ModemInfo.java b/telephony/java/android/telephony/ModemInfo.java
new file mode 100644
index 0000000..c0833af
--- /dev/null
+++ b/telephony/java/android/telephony/ModemInfo.java
@@ -0,0 +1,109 @@
+/*
+ * 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 android.telephony;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Objects;
+
+/**
+ * Information of a single logical modem indicating
+ * its id, supported rats and whether it supports voice or data, etc.
+ * @hide
+ */
+public class ModemInfo implements Parcelable {
+ public final int modemId;
+ public final int rat; /* bitset */
+ public final boolean isVoiceSupported;
+ public final boolean isDataSupported;
+
+ // TODO b/121394331: Clean up this class after V1_1.PhoneCapability cleanup.
+ public ModemInfo(int modemId) {
+ this(modemId, 0, true, true);
+ }
+
+ public ModemInfo(int modemId, int rat, boolean isVoiceSupported, boolean isDataSupported) {
+ this.modemId = modemId;
+ this.rat = rat;
+ this.isVoiceSupported = isVoiceSupported;
+ this.isDataSupported = isDataSupported;
+ }
+
+ public ModemInfo(Parcel in) {
+ modemId = in.readInt();
+ rat = in.readInt();
+ isVoiceSupported = in.readBoolean();
+ isDataSupported = in.readBoolean();
+ }
+
+ @Override
+ public String toString() {
+ return "modemId=" + modemId + " rat=" + rat + " isVoiceSupported:" + isVoiceSupported
+ + " isDataSupported:" + isDataSupported;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(modemId, rat, isVoiceSupported, isDataSupported);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (o == null || !(o instanceof ModemInfo) || hashCode() != o.hashCode()) {
+ return false;
+ }
+
+ if (this == o) {
+ return true;
+ }
+
+ ModemInfo s = (ModemInfo) o;
+
+ return (modemId == s.modemId
+ && rat == s.rat
+ && isVoiceSupported == s.isVoiceSupported
+ && isDataSupported == s.isDataSupported);
+ }
+
+ /**
+ * {@link Parcelable#describeContents}
+ */
+ public @ContentsFlags int describeContents() {
+ return 0;
+ }
+
+ /**
+ * {@link Parcelable#writeToParcel}
+ */
+ public void writeToParcel(Parcel dest, @WriteFlags int flags) {
+ dest.writeInt(modemId);
+ dest.writeInt(rat);
+ dest.writeBoolean(isVoiceSupported);
+ dest.writeBoolean(isDataSupported);
+ }
+
+ public static final @android.annotation.NonNull Parcelable.Creator<ModemInfo> CREATOR = new Parcelable.Creator() {
+ public ModemInfo createFromParcel(Parcel in) {
+ return new ModemInfo(in);
+ }
+
+ public ModemInfo[] newArray(int size) {
+ return new ModemInfo[size];
+ }
+ };
+}
diff --git a/telephony/java/android/telephony/PhoneCapability.java b/telephony/java/android/telephony/PhoneCapability.java
index c66aceb..238766a 100644
--- a/telephony/java/android/telephony/PhoneCapability.java
+++ b/telephony/java/android/telephony/PhoneCapability.java
@@ -16,20 +16,12 @@
package android.telephony;
-import android.annotation.LongDef;
import android.annotation.NonNull;
-import android.annotation.Nullable;
import android.os.Parcel;
import android.os.Parcelable;
-import android.telephony.AccessNetworkConstants.AccessNetworkType;
-import android.telephony.AccessNetworkConstants.RadioAccessNetworkType;
-import android.telephony.TelephonyManager.NetworkTypeBitMask;
-import com.android.internal.telephony.util.TelephonyUtils;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.List;
import java.util.Objects;
@@ -38,365 +30,68 @@
* are shared between those modems defined by list of modem IDs.
*/
public final class PhoneCapability implements Parcelable {
- /** Modem feature indicating 3GPP2 capability. */
- public static final long MODEM_FEATURE_3GPP2_REG = 1 << 0;
- /** Modem feature indicating 3GPP capability. */
- public static final long MODEM_FEATURE_3GPP_REG = 1 << 1;
- /** Modem feature indicating CDMA 2000 with EHRPD capability. */
- public static final long MODEM_FEATURE_CDMA2000_EHRPD_REG = 1 << 2;
- /** Modem feature indicating GSM capability. */
- public static final long MODEM_FEATURE_GERAN_REG = 1 << 3;
- /** Modem feature indicating UMTS capability. */
- public static final long MODEM_FEATURE_UTRAN_REG = 1 << 4;
- /** Modem feature indicating LTE capability. */
- public static final long MODEM_FEATURE_EUTRAN_REG = 1 << 5;
- /** Modem feature indicating 5G capability.*/
- public static final long MODEM_FEATURE_NGRAN_REG = 1 << 6;
- /** Modem feature indicating EN-DC capability. */
- public static final long MODEM_FEATURE_EUTRA_NR_DUAL_CONNECTIVITY_REG = 1 << 7;
- /** Modem feature indicating VoLTE capability (IMS registered). */
- public static final long MODEM_FEATURE_PS_VOICE_REG = 1 << 8;
- /** Modem feature indicating CS voice call capability. */
- public static final long MODEM_FEATURE_CS_VOICE_SESSION = 1 << 9;
- /** Modem feature indicating Internet connection capability. */
- public static final long MODEM_FEATURE_INTERACTIVE_DATA_SESSION = 1 << 10;
- /**
- * Modem feature indicating dedicated bearer capability.
- * For services that require a high level QoS (eg. VoLTE), the network can create
- * a dedicated bearer with the required QoS on top of an established default bearer.
- * This will provide a dedicated tunnel for one or more specific traffic types.
- */
- public static final long MODEM_FEATURE_DEDICATED_BEARER = 1 << 11;
- /** Modem feature indicating network scan capability. */
- public static final long MODEM_FEATURE_NETWORK_SCAN = 1 << 12;
- /** Modem feature indicating corresponding SIM has CDMA capability. */
- public static final long MODEM_FEATURE_CSIM = 1 << 13;
-
+ // Hardcoded default DSDS capability.
/** @hide */
- @LongDef(flag = true, prefix = {"MODEM_FEATURE_" }, value = {
- MODEM_FEATURE_3GPP2_REG,
- MODEM_FEATURE_3GPP_REG,
- MODEM_FEATURE_CDMA2000_EHRPD_REG,
- MODEM_FEATURE_GERAN_REG,
- MODEM_FEATURE_UTRAN_REG,
- MODEM_FEATURE_EUTRAN_REG,
- MODEM_FEATURE_NGRAN_REG,
- MODEM_FEATURE_EUTRA_NR_DUAL_CONNECTIVITY_REG,
- MODEM_FEATURE_PS_VOICE_REG,
- MODEM_FEATURE_CS_VOICE_SESSION,
- MODEM_FEATURE_INTERACTIVE_DATA_SESSION,
- MODEM_FEATURE_DEDICATED_BEARER,
- MODEM_FEATURE_NETWORK_SCAN,
- MODEM_FEATURE_CSIM,
- })
- @Retention(RetentionPolicy.SOURCE)
- public @interface ModemFeature {
- }
-
- /**
- * Hardcoded default DSDS capability.
- * @hide
- */
public static final PhoneCapability DEFAULT_DSDS_CAPABILITY;
- /**
- * Hardcoded default Single SIM single standby capability.
- * @hide
- */
+ // Hardcoded default Single SIM single standby capability.
+ /** @hide */
public static final PhoneCapability DEFAULT_SSSS_CAPABILITY;
static {
- List<List<Long>> capabilities = new ArrayList<>();
- List<Long> modem1 = new ArrayList<>();
- List<Long> modem2 = new ArrayList<>();
- modem1.add(MODEM_FEATURE_GERAN_REG | MODEM_FEATURE_UTRAN_REG | MODEM_FEATURE_EUTRAN_REG
- | MODEM_FEATURE_PS_VOICE_REG | MODEM_FEATURE_CS_VOICE_SESSION
- | MODEM_FEATURE_INTERACTIVE_DATA_SESSION | MODEM_FEATURE_DEDICATED_BEARER);
- modem2.add(MODEM_FEATURE_GERAN_REG | MODEM_FEATURE_UTRAN_REG | MODEM_FEATURE_EUTRAN_REG
- | MODEM_FEATURE_PS_VOICE_REG | MODEM_FEATURE_INTERACTIVE_DATA_SESSION
- | MODEM_FEATURE_DEDICATED_BEARER);
- capabilities.add(modem1);
- capabilities.add(modem2);
- List<String> uuids = new ArrayList<>();
- uuids.add("com.xxxx.lm0");
- uuids.add("com.xxxx.lm1");
- long rats = TelephonyManager.NETWORK_TYPE_BITMASK_GSM
- | TelephonyManager.NETWORK_TYPE_BITMASK_GPRS
- | TelephonyManager.NETWORK_TYPE_BITMASK_EDGE
- | TelephonyManager.NETWORK_TYPE_BITMASK_UMTS
- | TelephonyManager.NETWORK_TYPE_BITMASK_LTE;
- DEFAULT_DSDS_CAPABILITY = new PhoneCapability(0, 0, 0, 0, 0, rats, null, null, null, null,
- uuids, null, capabilities);
+ ModemInfo modemInfo1 = new ModemInfo(0, 0, true, true);
+ ModemInfo modemInfo2 = new ModemInfo(1, 0, true, true);
- capabilities = new ArrayList<>();
- capabilities.add(modem1);
- uuids = new ArrayList<>();
- uuids.add("com.xxxx.lm0");
- DEFAULT_SSSS_CAPABILITY = new PhoneCapability(0, 0, 0, 0, 0, rats, null, null, null, null,
- uuids, null, capabilities);
+ List<ModemInfo> logicalModemList = new ArrayList<>();
+ logicalModemList.add(modemInfo1);
+ logicalModemList.add(modemInfo2);
+ DEFAULT_DSDS_CAPABILITY = new PhoneCapability(1, 1, 0, logicalModemList, false);
+
+ logicalModemList = new ArrayList<>();
+ logicalModemList.add(modemInfo1);
+ DEFAULT_SSSS_CAPABILITY = new PhoneCapability(1, 1, 0, logicalModemList, false);
}
+ /** @hide */
+ public final int maxActiveVoiceCalls;
+ /** @hide */
+ public final int maxActiveData;
+ /** @hide */
+ public final int max5G;
+ /** @hide */
+ public final boolean validationBeforeSwitchSupported;
+ /** @hide */
+ public final List<ModemInfo> logicalModemList;
- private final int mUtranUeCategoryDl;
- private final int mUtranUeCategoryUl;
- private final int mEutranUeCategoryDl;
- private final int mEutranUeCategoryUl;
- private final long mPsDataConnectionLingerTimeMillis;
- private final @NetworkTypeBitMask long mSupportedRats;
- private final List<Integer> mGeranBands;
- private final List<Integer> mUtranBands;
- private final List<Integer> mEutranBands;
- private final List<Integer> mNgranBands;
- private final List<String> mLogicalModemUuids;
- private final List<SimSlotCapability> mSimSlotCapabilities;
- private final @ModemFeature List<List<Long>> mConcurrentFeaturesSupport;
-
- /**
- * Default constructor to create a PhoneCapability object.
- * @param utranUeCategoryDl 3GPP UE category for UTRAN downlink.
- * @param utranUeCategoryUl 3GPP UE category for UTRAN uplink.
- * @param eutranUeCategoryDl 3GPP UE category for EUTRAN downlink.
- * @param eutranUeCategoryUl 3GPP UE category for EUTRAN uplink.
- * @param psDataConnectionLingerTimeMillis length of the grace period to allow a smooth
- * "handover" between data connections.
- * @param supportedRats all radio access technologies this phone is capable of supporting.
- * @param geranBands list of supported {@link AccessNetworkConstants.GeranBand}.
- * @param utranBands list of supported {@link AccessNetworkConstants.UtranBand}.
- * @param eutranBands list of supported {@link AccessNetworkConstants.EutranBand}.
- * @param ngranBands list of supported {@link AccessNetworkConstants.NgranBands}.
- * @param logicalModemUuids list of logical modem UUIDs, typically of the form
- * "com.xxxx.lmX", where X is the logical modem ID.
- * @param simSlotCapabilities list of {@link SimSlotCapability} for the device
- * @param concurrentFeaturesSupport list of list of concurrently supportable modem feature sets.
- * @hide
- */
- public PhoneCapability(int utranUeCategoryDl, int utranUeCategoryUl, int eutranUeCategoryDl,
- int eutranUeCategoryUl, long psDataConnectionLingerTimeMillis,
- @NetworkTypeBitMask long supportedRats, @Nullable List<Integer> geranBands,
- @Nullable List<Integer> utranBands, @Nullable List<Integer> eutranBands,
- @Nullable List<Integer> ngranBands, @Nullable List<String> logicalModemUuids,
- @Nullable List<SimSlotCapability> simSlotCapabilities,
- @Nullable @ModemFeature List<List<Long>> concurrentFeaturesSupport) {
- this.mUtranUeCategoryDl = utranUeCategoryDl;
- this.mUtranUeCategoryUl = utranUeCategoryUl;
- this.mEutranUeCategoryDl = eutranUeCategoryDl;
- this.mEutranUeCategoryUl = eutranUeCategoryUl;
- this.mPsDataConnectionLingerTimeMillis = psDataConnectionLingerTimeMillis;
- this.mSupportedRats = supportedRats;
- this.mGeranBands = TelephonyUtils.emptyIfNull(geranBands);
- this.mUtranBands = TelephonyUtils.emptyIfNull(utranBands);
- this.mEutranBands = TelephonyUtils.emptyIfNull(eutranBands);
- this.mNgranBands = TelephonyUtils.emptyIfNull(ngranBands);
- this.mLogicalModemUuids = TelephonyUtils.emptyIfNull(logicalModemUuids);
- this.mSimSlotCapabilities = TelephonyUtils.emptyIfNull(simSlotCapabilities);
- this.mConcurrentFeaturesSupport = TelephonyUtils.emptyIfNull(concurrentFeaturesSupport);
- }
-
- private PhoneCapability(Parcel in) {
- mUtranUeCategoryDl = in.readInt();
- mUtranUeCategoryUl = in.readInt();
- mEutranUeCategoryDl = in.readInt();
- mEutranUeCategoryUl = in.readInt();
- mPsDataConnectionLingerTimeMillis = in.readLong();
- mSupportedRats = in.readLong();
- mGeranBands = new ArrayList<>();
- in.readList(mGeranBands, Integer.class.getClassLoader());
- mUtranBands = new ArrayList<>();
- in.readList(mUtranBands, Integer.class.getClassLoader());
- mEutranBands = new ArrayList<>();
- in.readList(mEutranBands, Integer.class.getClassLoader());
- mNgranBands = new ArrayList<>();
- in.readList(mNgranBands, Integer.class.getClassLoader());
- mLogicalModemUuids = in.createStringArrayList();
- mSimSlotCapabilities = in.createTypedArrayList(SimSlotCapability.CREATOR);
- int length = in.readInt();
- mConcurrentFeaturesSupport = new ArrayList<>();
- for (int i = 0; i < length; i++) {
- ArrayList<Long> feature = new ArrayList<>();
- in.readList(feature, Long.class.getClassLoader());
- mConcurrentFeaturesSupport.add(feature);
- }
- }
-
- /**
- * 3GPP UE category for a given Radio Access Network and direction.
- *
- * References are:
- * TS 25.306 Table 4.1a EUTRAN downlink
- * TS 25.306 Table 5.1a-2 EUTRAN uplink
- * TS 25.306 Table 5.1a UTRAN downlink
- * TS 25.306 Table 5.1g UTRAN uplink
- *
- * @param uplink true for uplink direction and false for downlink direction.
- * @param accessNetworkType accessNetworkType, defined in {@link AccessNetworkType}.
- * @return the UE category, or -1 if it is not supported.
- */
- public int getUeCategory(boolean uplink, @RadioAccessNetworkType int accessNetworkType) {
- if (uplink) {
- switch (accessNetworkType) {
- case AccessNetworkType.UTRAN: return mUtranUeCategoryUl;
- case AccessNetworkType.EUTRAN: return mEutranUeCategoryUl;
- default: return -1;
- }
- } else {
- switch (accessNetworkType) {
- case AccessNetworkType.UTRAN: return mUtranUeCategoryDl;
- case AccessNetworkType.EUTRAN: return mEutranUeCategoryDl;
- default: return -1;
- }
- }
- }
-
- /**
- * In cellular devices that support a greater number of logical modems than
- * Internet connections, some devices support a grace period to allow a smooth "handover"
- * between those connections. If that feature is supported, then this API will provide
- * the length of that grace period in milliseconds. If it is not supported, the default value
- * for the grace period is 0.
- * @return handover linger time in milliseconds, or 0 if it is not supported.
- */
- public long getPsDataConnectionLingerTimeMillis() {
- return mPsDataConnectionLingerTimeMillis;
- }
-
- /**
- * The radio access technologies this device is capable of supporting.
- * @return a bitfield of all supported network types, defined in {@link TelephonyManager}
- */
- public @NetworkTypeBitMask long getSupportedRats() {
- return mSupportedRats;
- }
-
- /**
- * List of supported cellular bands for the given accessNetworkType.
- * @param accessNetworkType accessNetworkType, defined in {@link AccessNetworkType}.
- * @return a list of bands, or an empty list if the access network type is unsupported.
- */
- public @NonNull List<Integer> getBands(@RadioAccessNetworkType int accessNetworkType) {
- switch (accessNetworkType) {
- case AccessNetworkType.GERAN: return mGeranBands;
- case AccessNetworkType.UTRAN: return mUtranBands;
- case AccessNetworkType.EUTRAN: return mEutranBands;
- case AccessNetworkType.NGRAN: return mNgranBands;
- default: return new ArrayList<>();
- }
- }
-
- /**
- * List of logical modem UUIDs, each typically "com.xxxx.lmX", where X is the logical modem ID.
- * @return a list of modem UUIDs, one for every logical modem the device has.
- */
- public @NonNull List<String> getLogicalModemUuids() {
- return mLogicalModemUuids;
- }
-
- /**
- * List of {@link SimSlotCapability} for the device. The order of SIMs corresponds to the
- * order of modems in {@link #getLogicalModemUuids}.
- * @return a list of SIM slot capabilities, one for every SIM slot the device has.
- */
- public @NonNull List<SimSlotCapability> getSimSlotCapabilities() {
- return mSimSlotCapabilities;
- }
-
- /**
- * A List of Lists of concurrently supportable modem feature sets.
- *
- * Each entry in the top-level list is an independent configuration across all modems
- * that describes the capabilities of the device as a whole.
- *
- * Each entry in the second-level list is a bitfield of ModemFeatures that describes
- * the capabilities for a single modem. In the second-level list, the order of the modems
- * corresponds to order of the UUIDs in {@link #getLogicalModemUuids}.
- *
- * For symmetric capabilities that can only be active on one modem at a time, there will be
- * multiple configurations (equal to the number of modems) that shows it active on each modem.
- * For asymmetric capabilities that are only available on one of the modems, all configurations
- * will have that capability on just that one modem.
- *
- * The example below shows the concurrentFeaturesSupport for a 3-modem device with
- * theoretical capabilities SYMMETRIC (available on all modems, but only one at a time) and
- * ASYMMETRIC (only available on the first modem):
- * {
- * Configuration 1: ASYMMETRIC and SYMMETRIC on modem 1, modem 2 empty, modem 3 empty
- * {(ASYMMETRIC | SYMMETRIC), (), ()},
- *
- * Configuration 2: ASYMMETRIC on modem 1, SYMMETRIC on modem 2, modem 3 empty
- * {(ASYMMETRIC), (SYMMETRIC), ()},
- *
- * Configuration 3: ASYMMETRIC on modem 1, modem 2 empty, SYMMETRIC on modem 3
- * {(ASYMMETRIC), (), (SYMMETRIC)}
- * }
- *
- * @return List of all concurrently supportable modem features.
- */
- public @NonNull @ModemFeature List<List<Long>> getConcurrentFeaturesSupport() {
- return mConcurrentFeaturesSupport;
- }
-
- /**
- * How many modems can simultaneously have PS attached.
- * @return maximum number of active PS voice connections.
- */
- public int getMaxActivePsVoice() {
- return countFeature(MODEM_FEATURE_PS_VOICE_REG);
- }
-
- /**
- * How many modems can simultaneously support active data connections.
- * For DSDS, this will be 1, and for DSDA this will be 2.
- * @return maximum number of active Internet data sessions.
- */
- public int getMaxActiveInternetData() {
- return countFeature(MODEM_FEATURE_INTERACTIVE_DATA_SESSION);
- }
-
- /**
- * How many modems can simultaneously have dedicated bearer capability.
- * @return maximum number of active dedicated bearers.
- */
- public int getMaxActiveDedicatedBearers() {
- return countFeature(MODEM_FEATURE_DEDICATED_BEARER);
- }
-
- /**
- * Whether the CBRS band 48 is supported or not.
- * @return true if any RadioAccessNetwork supports CBRS and false if none do.
- * @hide
- */
- public boolean isCbrsSupported() {
- return mEutranBands.contains(AccessNetworkConstants.EutranBand.BAND_48)
- || mNgranBands.contains(AccessNetworkConstants.NgranBands.BAND_48);
- }
-
- private int countFeature(@ModemFeature long feature) {
- int count = 0;
- for (long featureSet : mConcurrentFeaturesSupport.get(0)) {
- if ((featureSet & feature) != 0) {
- count++;
- }
- }
- return count;
+ /** @hide */
+ public PhoneCapability(int maxActiveVoiceCalls, int maxActiveData, int max5G,
+ List<ModemInfo> logicalModemList, boolean validationBeforeSwitchSupported) {
+ this.maxActiveVoiceCalls = maxActiveVoiceCalls;
+ this.maxActiveData = maxActiveData;
+ this.max5G = max5G;
+ // Make sure it's not null.
+ this.logicalModemList = logicalModemList == null ? new ArrayList<>() : logicalModemList;
+ this.validationBeforeSwitchSupported = validationBeforeSwitchSupported;
}
@Override
public String toString() {
- return "utranUeCategoryDl=" + mUtranUeCategoryDl
- + " utranUeCategoryUl=" + mUtranUeCategoryUl
- + " eutranUeCategoryDl=" + mEutranUeCategoryDl
- + " eutranUeCategoryUl=" + mEutranUeCategoryUl
- + " psDataConnectionLingerTimeMillis=" + mPsDataConnectionLingerTimeMillis
- + " supportedRats=" + mSupportedRats + " geranBands=" + mGeranBands
- + " utranBands=" + mUtranBands + " eutranBands=" + mEutranBands
- + " ngranBands=" + mNgranBands + " logicalModemUuids=" + mLogicalModemUuids
- + " simSlotCapabilities=" + mSimSlotCapabilities
- + " concurrentFeaturesSupport=" + mConcurrentFeaturesSupport;
+ return "maxActiveVoiceCalls=" + maxActiveVoiceCalls + " maxActiveData=" + maxActiveData
+ + " max5G=" + max5G + "logicalModemList:"
+ + Arrays.toString(logicalModemList.toArray());
+ }
+
+ private PhoneCapability(Parcel in) {
+ maxActiveVoiceCalls = in.readInt();
+ maxActiveData = in.readInt();
+ max5G = in.readInt();
+ validationBeforeSwitchSupported = in.readBoolean();
+ logicalModemList = new ArrayList<>();
+ in.readList(logicalModemList, ModemInfo.class.getClassLoader());
}
@Override
public int hashCode() {
- return Objects.hash(mUtranUeCategoryDl, mUtranUeCategoryUl, mEutranUeCategoryDl,
- mEutranUeCategoryUl, mPsDataConnectionLingerTimeMillis, mSupportedRats, mGeranBands,
- mUtranBands, mEutranBands, mNgranBands, mLogicalModemUuids, mSimSlotCapabilities,
- mConcurrentFeaturesSupport);
+ return Objects.hash(maxActiveVoiceCalls, maxActiveData, max5G, logicalModemList,
+ validationBeforeSwitchSupported);
}
@Override
@@ -411,19 +106,11 @@
PhoneCapability s = (PhoneCapability) o;
- return (mUtranUeCategoryDl == s.mUtranUeCategoryDl
- && mUtranUeCategoryUl == s.mUtranUeCategoryUl
- && mEutranUeCategoryDl == s.mEutranUeCategoryDl
- && mEutranUeCategoryUl == s.mEutranUeCategoryUl
- && mPsDataConnectionLingerTimeMillis == s.mPsDataConnectionLingerTimeMillis
- && mSupportedRats == s.mSupportedRats
- && mGeranBands.equals(s.mGeranBands)
- && mUtranBands.equals(s.mUtranBands)
- && mEutranBands.equals(s.mEutranBands)
- && mNgranBands.equals(s.mNgranBands)
- && mLogicalModemUuids.equals(s.mLogicalModemUuids)
- && mSimSlotCapabilities.equals(s.mSimSlotCapabilities)
- && mConcurrentFeaturesSupport.equals(s.mConcurrentFeaturesSupport));
+ return (maxActiveVoiceCalls == s.maxActiveVoiceCalls
+ && maxActiveData == s.maxActiveData
+ && max5G == s.max5G
+ && validationBeforeSwitchSupported == s.validationBeforeSwitchSupported
+ && logicalModemList.equals(s.logicalModemList));
}
/**
@@ -437,32 +124,20 @@
* {@link Parcelable#writeToParcel}
*/
public void writeToParcel(@NonNull Parcel dest, @Parcelable.WriteFlags int flags) {
- dest.writeInt(mUtranUeCategoryDl);
- dest.writeInt(mUtranUeCategoryUl);
- dest.writeInt(mEutranUeCategoryDl);
- dest.writeInt(mEutranUeCategoryUl);
- dest.writeLong(mPsDataConnectionLingerTimeMillis);
- dest.writeLong(mSupportedRats);
- dest.writeList(mGeranBands);
- dest.writeList(mUtranBands);
- dest.writeList(mEutranBands);
- dest.writeList(mNgranBands);
- dest.writeStringList(mLogicalModemUuids);
- dest.writeTypedList(mSimSlotCapabilities);
- dest.writeInt(mConcurrentFeaturesSupport.size());
- for (List<Long> feature : mConcurrentFeaturesSupport) {
- dest.writeList(feature);
- }
+ dest.writeInt(maxActiveVoiceCalls);
+ dest.writeInt(maxActiveData);
+ dest.writeInt(max5G);
+ dest.writeBoolean(validationBeforeSwitchSupported);
+ dest.writeList(logicalModemList);
}
- public static final @NonNull Parcelable.Creator<PhoneCapability> CREATOR =
- new Parcelable.Creator() {
- public PhoneCapability createFromParcel(Parcel in) {
- return new PhoneCapability(in);
- }
+ public static final @android.annotation.NonNull Parcelable.Creator<PhoneCapability> CREATOR = new Parcelable.Creator() {
+ public PhoneCapability createFromParcel(Parcel in) {
+ return new PhoneCapability(in);
+ }
- public PhoneCapability[] newArray(int size) {
- return new PhoneCapability[size];
- }
- };
+ public PhoneCapability[] newArray(int size) {
+ return new PhoneCapability[size];
+ }
+ };
}
diff --git a/telephony/java/android/telephony/ServiceState.java b/telephony/java/android/telephony/ServiceState.java
index 9ef361d..a5a1ebc 100644
--- a/telephony/java/android/telephony/ServiceState.java
+++ b/telephony/java/android/telephony/ServiceState.java
@@ -317,6 +317,14 @@
*/
public static final int UNKNOWN_ID = -1;
+ /**
+ * A parcelable extra used with {@link Intent#ACTION_SERVICE_STATE} representing the service
+ * state.
+ * @hide
+ */
+ private static final String EXTRA_SERVICE_STATE = "android.intent.extra.SERVICE_STATE";
+
+
private String mOperatorAlphaLong;
private String mOperatorAlphaShort;
private String mOperatorNumeric;
@@ -353,6 +361,7 @@
private String mOperatorAlphaLongRaw;
private String mOperatorAlphaShortRaw;
+ private boolean mIsDataRoamingFromRegistration;
private boolean mIsIwlanPreferred;
/**
@@ -438,6 +447,7 @@
mNrFrequencyRange = s.mNrFrequencyRange;
mOperatorAlphaLongRaw = s.mOperatorAlphaLongRaw;
mOperatorAlphaShortRaw = s.mOperatorAlphaShortRaw;
+ mIsDataRoamingFromRegistration = s.mIsDataRoamingFromRegistration;
mIsIwlanPreferred = s.mIsIwlanPreferred;
}
@@ -472,6 +482,7 @@
mNrFrequencyRange = in.readInt();
mOperatorAlphaLongRaw = in.readString();
mOperatorAlphaShortRaw = in.readString();
+ mIsDataRoamingFromRegistration = in.readBoolean();
mIsIwlanPreferred = in.readBoolean();
}
@@ -499,6 +510,7 @@
out.writeInt(mNrFrequencyRange);
out.writeString(mOperatorAlphaLongRaw);
out.writeString(mOperatorAlphaShortRaw);
+ out.writeBoolean(mIsDataRoamingFromRegistration);
out.writeBoolean(mIsIwlanPreferred);
}
@@ -649,7 +661,9 @@
}
/**
- * Get current data network roaming type
+ * Get whether the current data network is roaming.
+ * This value may be overwritten by resource overlay or carrier configuration.
+ * @see #getDataRoamingFromRegistration() to get the value from the network registration.
* @return roaming type
* @hide
*/
@@ -659,18 +673,25 @@
}
/**
- * Get whether data network registration state is roaming
+ * Set whether the data network registration state is roaming.
+ * This should only be set to the roaming value received
+ * once the data registration phase has completed.
+ * @hide
+ */
+ public void setDataRoamingFromRegistration(boolean dataRoaming) {
+ mIsDataRoamingFromRegistration = dataRoaming;
+ }
+
+ /**
+ * Get whether data network registration state is roaming.
+ * This value is set directly from the modem and will not be overwritten
+ * by resource overlay or carrier configuration.
* @return true if registration indicates roaming, false otherwise
* @hide
*/
+ @SystemApi
public boolean getDataRoamingFromRegistration() {
- final NetworkRegistrationInfo regState = getNetworkRegistrationInfo(
- NetworkRegistrationInfo.DOMAIN_PS, AccessNetworkConstants.TRANSPORT_TYPE_WWAN);
- if (regState != null) {
- return regState.getRegistrationState()
- == NetworkRegistrationInfo.REGISTRATION_STATE_ROAMING;
- }
- return false;
+ return mIsDataRoamingFromRegistration;
}
/**
@@ -873,6 +894,7 @@
mNrFrequencyRange,
mOperatorAlphaLongRaw,
mOperatorAlphaShortRaw,
+ mIsDataRoamingFromRegistration,
mIsIwlanPreferred);
}
}
@@ -903,6 +925,7 @@
&& mNetworkRegistrationInfos.size() == s.mNetworkRegistrationInfos.size()
&& mNetworkRegistrationInfos.containsAll(s.mNetworkRegistrationInfos)
&& mNrFrequencyRange == s.mNrFrequencyRange
+ && mIsDataRoamingFromRegistration == s.mIsDataRoamingFromRegistration
&& mIsIwlanPreferred == s.mIsIwlanPreferred;
}
}
@@ -1062,6 +1085,8 @@
.append(", mNrFrequencyRange=").append(mNrFrequencyRange)
.append(", mOperatorAlphaLongRaw=").append(mOperatorAlphaLongRaw)
.append(", mOperatorAlphaShortRaw=").append(mOperatorAlphaShortRaw)
+ .append(", mIsDataRoamingFromRegistration=")
+ .append(mIsDataRoamingFromRegistration)
.append(", mIsIwlanPreferred=").append(mIsIwlanPreferred)
.append("}").toString();
}
@@ -1102,6 +1127,7 @@
}
mOperatorAlphaLongRaw = null;
mOperatorAlphaShortRaw = null;
+ mIsDataRoamingFromRegistration = false;
mIsIwlanPreferred = false;
}
@@ -1274,7 +1300,7 @@
*/
@UnsupportedAppUsage
private void setFromNotifierBundle(Bundle m) {
- ServiceState ssFromBundle = m.getParcelable(Intent.EXTRA_SERVICE_STATE);
+ ServiceState ssFromBundle = m.getParcelable(EXTRA_SERVICE_STATE);
if (ssFromBundle != null) {
copyFrom(ssFromBundle);
}
@@ -1292,7 +1318,7 @@
*/
@SystemApi
public void fillInNotifierBundle(@NonNull Bundle m) {
- m.putParcelable(Intent.EXTRA_SERVICE_STATE, this);
+ m.putParcelable(EXTRA_SERVICE_STATE, this);
// serviceState already consists of below entries.
// for backward compatibility, we continue fill in below entries.
m.putInt("voiceRegState", mVoiceRegState);
diff --git a/telephony/java/android/telephony/SimSlotCapability.java b/telephony/java/android/telephony/SimSlotCapability.java
deleted file mode 100644
index 3d38d04..0000000
--- a/telephony/java/android/telephony/SimSlotCapability.java
+++ /dev/null
@@ -1,130 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.telephony;
-
-import android.annotation.IntDef;
-import android.annotation.NonNull;
-import android.os.Parcel;
-import android.os.Parcelable;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.util.Objects;
-
-/**
- * Capabilities for a SIM Slot.
- */
-public final class SimSlotCapability implements Parcelable {
- /** Slot type for UICC (removable SIM). */
- public static final int SLOT_TYPE_UICC = 1;
- /** Slot type for iUICC/iSIM (integrated SIM). */
- public static final int SLOT_TYPE_IUICC = 2;
- /** Slot type for eUICC/eSIM (embedded SIM). */
- public static final int SLOT_TYPE_EUICC = 3;
- /** Slot type for soft SIM (no physical SIM). */
- public static final int SLOT_TYPE_SOFT_SIM = 4;
-
- /** @hide */
- @IntDef(prefix = {"SLOT_TYPE_" }, value = {
- SLOT_TYPE_UICC,
- SLOT_TYPE_IUICC,
- SLOT_TYPE_EUICC,
- SLOT_TYPE_SOFT_SIM,
- })
- @Retention(RetentionPolicy.SOURCE)
- public @interface SlotType {
- }
-
- private final int mPhysicalSlotIndex;
- private final int mSlotType;
-
- /** @hide */
- public SimSlotCapability(int physicalSlotId, int slotType) {
- this.mPhysicalSlotIndex = physicalSlotId;
- this.mSlotType = slotType;
- }
-
- private SimSlotCapability(Parcel in) {
- mPhysicalSlotIndex = in.readInt();
- mSlotType = in.readInt();
- }
-
- /**
- * @return physical SIM slot index
- */
- public int getPhysicalSlotIndex() {
- return mPhysicalSlotIndex;
- }
-
- /**
- * @return type of SIM {@link SlotType}
- */
- public @SlotType int getSlotType() {
- return mSlotType;
- }
-
- @Override
- public String toString() {
- return "mPhysicalSlotIndex=" + mPhysicalSlotIndex + " slotType=" + mSlotType;
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(mPhysicalSlotIndex, mSlotType);
- }
-
- @Override
- public boolean equals(Object o) {
- if (o == null || !(o instanceof SimSlotCapability) || hashCode() != o.hashCode()) {
- return false;
- }
-
- if (this == o) {
- return true;
- }
-
- SimSlotCapability s = (SimSlotCapability) o;
-
- return (mPhysicalSlotIndex == s.mPhysicalSlotIndex && mSlotType == s.mSlotType);
- }
-
- /**
- * {@link Parcelable#describeContents}
- */
- public @ContentsFlags int describeContents() {
- return 0;
- }
-
- /**
- * {@link Parcelable#writeToParcel}
- */
- public void writeToParcel(@NonNull Parcel dest, @WriteFlags int flags) {
- dest.writeInt(mPhysicalSlotIndex);
- dest.writeInt(mSlotType);
- }
-
- public static final @NonNull Parcelable.Creator<SimSlotCapability> CREATOR =
- new Parcelable.Creator() {
- public SimSlotCapability createFromParcel(Parcel in) {
- return new SimSlotCapability(in);
- }
-
- public SimSlotCapability[] newArray(int size) {
- return new SimSlotCapability[size];
- }
- };
-}
diff --git a/telephony/java/android/telephony/SmsManager.java b/telephony/java/android/telephony/SmsManager.java
index e7b2613..f86eeb2 100644
--- a/telephony/java/android/telephony/SmsManager.java
+++ b/telephony/java/android/telephony/SmsManager.java
@@ -2015,8 +2015,12 @@
/**
* Gets the total capacity of SMS storage on RUIM and SIM cards
+ * <p>
+ * This is the number of 176 byte EF-SMS records which can be stored on the RUIM or SIM card.
+ * <p>
+ * See 3GPP TS 31.102 - 4.2.25 - EF-SMS for more information
*
- * @return the total capacity count of SMS on RUIM and SIM cards
+ * @return the total number of SMS records which can be stored on the RUIM or SIM cards.
* @hide
*/
@SystemApi
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index 1cf8f1e..ef03545 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -514,13 +514,6 @@
public static final String ISO_COUNTRY_CODE = SimInfo.ISO_COUNTRY_CODE;
/**
- * TelephonyProvider column name for the sim provisioning status associated with a SIM.
- * <P>Type: INTEGER (int)</P>
- * @hide
- */
- public static final String SIM_PROVISIONING_STATUS = SimInfo.SIM_PROVISIONING_STATUS;
-
- /**
* TelephonyProvider column name for whether a subscription is embedded (that is, present on an
* eSIM).
* <p>Type: INTEGER (int), 1 for embedded or 0 for non-embedded.
@@ -1303,6 +1296,11 @@
* The records will be sorted by {@link SubscriptionInfo#getSimSlotIndex}
* then by {@link SubscriptionInfo#getSubscriptionId}.
*
+ * Hidden subscriptions refer to those are not meant visible to the users.
+ * For example, an opportunistic subscription that is grouped with other
+ * subscriptions should remain invisible to users as they are only functionally
+ * supplementary to primary ones.
+ *
* <p>Requires Permission: {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
* or that the calling app has carrier privileges (see
* {@link TelephonyManager#hasCarrierPrivileges}). In the latter case, only records accessible
@@ -2153,20 +2151,35 @@
}
/**
- * TODO(b/137102918) Make this static, tests use this as an instance method currently.
+ * Get visible subscription Id(s) of the currently active SIM(s).
*
* @return the list of subId's that are active,
- * is never null but the length maybe 0.
+ * is never null but the length may be 0.
* @hide
*/
- @UnsupportedAppUsage
+ @SystemApi
public @NonNull int[] getActiveSubscriptionIdList() {
return getActiveSubscriptionIdList(/* visibleOnly */ true);
}
/**
- * TODO(b/137102918) Make this static, tests use this as an instance method currently.
+ * Get both hidden and visible subscription Id(s) of the currently active SIM(s).
*
+ * Hidden subscriptions refer to those are not meant visible to the users.
+ * For example, an opportunistic subscription that is grouped with other
+ * subscriptions should remain invisible to users as they are only functionally
+ * supplementary to primary ones.
+ *
+ * @return the list of subId's that are active,
+ * is never null but the length may be 0.
+ * @hide
+ */
+ @SystemApi
+ public @NonNull int[] getActiveAndHiddenSubscriptionIdList() {
+ return getActiveSubscriptionIdList(/* visibleOnly */false);
+ }
+
+ /**
* @return a non-null list of subId's that are active.
*
* @hide
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 2c85b27..7f7833b 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -45,10 +45,8 @@
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
-import android.content.pm.PackageManager;
import android.database.Cursor;
import android.net.ConnectivityManager;
-import android.net.NetworkStats;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Binder;
@@ -1725,24 +1723,6 @@
//
/**
- * Returns the {@link PhoneCapability} for the device or null if it is not available.
- * <p>
- * Requires Permission: READ_PHONE_STATE or that the calling app has
- * carrier privileges (see {@link #hasCarrierPrivileges}).
- */
- @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
- @Nullable
- public PhoneCapability getPhoneCapability() {
- try {
- ITelephony telephony = getITelephony();
- return telephony == null ? null :
- telephony.getPhoneCapability(getSubId(), getOpPackageName(), getFeatureId());
- } catch (RemoteException ex) {
- return null;
- }
- }
-
- /**
* Returns the software version number for the device, for example,
* the IMEI/SV for GSM phones. Return null if the software version is
* not available.
@@ -3802,21 +3782,20 @@
}
/**
- * Return if the current radio is LTE on CDMA. This is a tri-state return value as for a period
- * of time the mode may be unknown.
+ * Return if the current radio has global mode enabled, meaning it supports
+ * both 3GPP and 3GPP2 radio technologies at the same time.
*
* <p>If this object has been created with {@link #createForSubscriptionId}, applies to the
- * given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultSubscriptionId()}
+ * given subId. Otherwise, applies to {@link SubscriptionManager#getDefaultSubscriptionId()}.
*
- * @return {@link PhoneConstants#LTE_ON_CDMA_UNKNOWN}, {@link PhoneConstants#LTE_ON_CDMA_FALSE}
- * or {@link PhoneConstants#LTE_ON_CDMA_TRUE}
- *
+ * @return {@code true} if global mode is enabled
+ * {@code false} if global mode is not enabled or unknown
* @hide
*/
- @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
- @UnsupportedAppUsage
- public int getLteOnCdmaMode() {
- return getLteOnCdmaMode(getSubId());
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
+ public boolean isGlobalModeEnabled() {
+ return getLteOnCdmaMode(getSubId()) == PhoneConstants.LTE_ON_CDMA_TRUE;
}
/**
@@ -3829,7 +3808,7 @@
* or {@link PhoneConstants#LTE_ON_CDMA_TRUE}
* @hide
*/
- @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
+ @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
@UnsupportedAppUsage
public int getLteOnCdmaMode(int subId) {
try {
@@ -6042,9 +6021,11 @@
* @param AID Application id. See ETSI 102.221 and 101.220.
* @param p2 P2 parameter (described in ISO 7816-4).
* @return an IccOpenLogicalChannelResponse object.
- * @deprecated Use {@link android.se.omapi.SEService} APIs instead.
+ * @deprecated Use {@link android.se.omapi.SEService} APIs instead. See
+ * {@link android.se.omapi.SEService#getUiccReader(int)},
+ * {@link android.se.omapi.Reader#openSession()},
+ * {@link android.se.omapi.Session#openLogicalChannel(byte[], byte)}.
*/
- // TODO(b/147153909): Update Javadoc to link to specific SEService API once integrated.
@Deprecated
public IccOpenLogicalChannelResponse iccOpenLogicalChannel(String AID, int p2) {
return iccOpenLogicalChannel(getSubId(), AID, p2);
@@ -6076,9 +6057,11 @@
* @param p2 P2 parameter (described in ISO 7816-4).
* @return an IccOpenLogicalChannelResponse object.
* @hide
- * @deprecated Use {@link android.se.omapi.SEService} APIs instead.
+ * @deprecated Use {@link android.se.omapi.SEService} APIs instead. See
+ * {@link android.se.omapi.SEService#getUiccReader(int)},
+ * {@link android.se.omapi.Reader#openSession()},
+ * {@link android.se.omapi.Session#openLogicalChannel(byte[], byte)}.
*/
- // TODO(b/147153909): Update Javadoc to link to specific SEService API once integrated.
@Deprecated
public IccOpenLogicalChannelResponse iccOpenLogicalChannel(int subId, String AID, int p2) {
try {
@@ -6107,9 +6090,9 @@
* iccOpenLogicalChannel.
* @return true if the channel was closed successfully.
* @hide
- * @deprecated Use {@link android.se.omapi.SEService} APIs instead.
+ * @deprecated Use {@link android.se.omapi.SEService} APIs instead. See
+ * {@link android.se.omapi.Channel#close()}.
*/
- // TODO(b/147153909): Update Javadoc to link to specific SEService API once integrated.
@Deprecated
@RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
@SystemApi
@@ -6137,9 +6120,9 @@
* @param channel is the channel id to be closed as returned by a successful
* iccOpenLogicalChannel.
* @return true if the channel was closed successfully.
- * @deprecated Use {@link android.se.omapi.SEService} APIs instead.
+ * @deprecated Use {@link android.se.omapi.SEService} APIs instead. See
+ * {@link android.se.omapi.Channel#close()}.
*/
- // TODO(b/147153909): Update Javadoc to link to specific SEService API once integrated.
@Deprecated
public boolean iccCloseLogicalChannel(int channel) {
return iccCloseLogicalChannel(getSubId(), channel);
@@ -6159,9 +6142,9 @@
* iccOpenLogicalChannel.
* @return true if the channel was closed successfully.
* @hide
- * @deprecated Use {@link android.se.omapi.SEService} APIs instead.
+ * @deprecated Use {@link android.se.omapi.SEService} APIs instead. See
+ * {@link android.se.omapi.Channel#close()}.
*/
- // TODO(b/147153909): Update Javadoc to link to specific SEService API once integrated.
@Deprecated
public boolean iccCloseLogicalChannel(int subId, int channel) {
try {
@@ -6198,9 +6181,9 @@
* @return The APDU response from the ICC card with the status appended at the end, or null if
* there is an issue connecting to the Telephony service.
* @hide
- * @deprecated Use {@link android.se.omapi.SEService} APIs instead.
+ * @deprecated Use {@link android.se.omapi.SEService} APIs instead. See
+ * {@link android.se.omapi.Channel#transmit(byte[])}.
*/
- // TODO(b/147153909): Update Javadoc to link to specific SEService API once integrated.
@Deprecated
@RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
@SystemApi
@@ -6239,9 +6222,9 @@
* @param data Data to be sent with the APDU.
* @return The APDU response from the ICC card with the status appended at
* the end.
- * @deprecated Use {@link android.se.omapi.SEService} APIs instead.
+ * @deprecated Use {@link android.se.omapi.SEService} APIs instead. See
+ * {@link android.se.omapi.Channel#transmit(byte[])}.
*/
- // TODO(b/147153909): Update Javadoc to link to specific SEService API once integrated.
@Deprecated
public String iccTransmitApduLogicalChannel(int channel, int cla,
int instruction, int p1, int p2, int p3, String data) {
@@ -6271,9 +6254,9 @@
* @return The APDU response from the ICC card with the status appended at
* the end.
* @hide
- * @deprecated Use {@link android.se.omapi.SEService} APIs instead.
+ * @deprecated Use {@link android.se.omapi.SEService} APIs instead. See
+ * {@link android.se.omapi.Channel#transmit(byte[])}.
*/
- // TODO(b/147153909): Update Javadoc to link to specific SEService API once integrated.
@Deprecated
public String iccTransmitApduLogicalChannel(int subId, int channel, int cla,
int instruction, int p1, int p2, int p3, String data) {
@@ -6310,9 +6293,12 @@
* @return The APDU response from the ICC card with the status appended at
* the end.
* @hide
- * @deprecated Use {@link android.se.omapi.SEService} APIs instead.
+ * @deprecated Use {@link android.se.omapi.SEService} APIs instead. See
+ * {@link android.se.omapi.SEService#getUiccReader(int)},
+ * {@link android.se.omapi.Reader#openSession()},
+ * {@link android.se.omapi.Session#openBasicChannel(byte[], byte)},
+ * {@link android.se.omapi.Channel#transmit(byte[])}.
*/
- // TODO(b/147153909): Update Javadoc to link to specific SEService API once integrated.
@Deprecated
@RequiresPermission(Manifest.permission.MODIFY_PHONE_STATE)
@SystemApi
@@ -6349,9 +6335,12 @@
* @param data Data to be sent with the APDU.
* @return The APDU response from the ICC card with the status appended at
* the end.
- * @deprecated Use {@link android.se.omapi.SEService} APIs instead.
+ * @deprecated Use {@link android.se.omapi.SEService} APIs instead. See
+ * {@link android.se.omapi.SEService#getUiccReader(int)},
+ * {@link android.se.omapi.Reader#openSession()},
+ * {@link android.se.omapi.Session#openBasicChannel(byte[], byte)},
+ * {@link android.se.omapi.Channel#transmit(byte[])}.
*/
- // TODO(b/147153909): Update Javadoc to link to specific SEService API once integrated.
@Deprecated
public String iccTransmitApduBasicChannel(int cla,
int instruction, int p1, int p2, int p3, String data) {
@@ -6379,9 +6368,12 @@
* @return The APDU response from the ICC card with the status appended at
* the end.
* @hide
- * @deprecated Use {@link android.se.omapi.SEService} APIs instead.
+ * @deprecated Use {@link android.se.omapi.SEService} APIs instead. See
+ * {@link android.se.omapi.SEService#getUiccReader(int)},
+ * {@link android.se.omapi.Reader#openSession()},
+ * {@link android.se.omapi.Session#openBasicChannel(byte[], byte)},
+ * {@link android.se.omapi.Channel#transmit(byte[])}.
*/
- // TODO(b/147153909): Update Javadoc to link to specific SEService API once integrated.
@Deprecated
public String iccTransmitApduBasicChannel(int subId, int cla,
int instruction, int p1, int p2, int p3, String data) {
@@ -6410,9 +6402,12 @@
* @param p3 P3 value of the APDU command.
* @param filePath
* @return The APDU response.
- * @deprecated Use {@link android.se.omapi.SEService} APIs instead.
+ * @deprecated Use {@link android.se.omapi.SEService} APIs instead. See
+ * {@link android.se.omapi.SEService#getUiccReader(int)},
+ * {@link android.se.omapi.Reader#openSession()},
+ * {@link android.se.omapi.Session#openBasicChannel(byte[], byte)},
+ * {@link android.se.omapi.Channel#transmit(byte[])}.
*/
- // TODO(b/147153909): Update Javadoc to link to specific SEService API once integrated.
@Deprecated
public byte[] iccExchangeSimIO(int fileID, int command, int p1, int p2, int p3,
String filePath) {
@@ -6435,9 +6430,12 @@
* @param filePath
* @return The APDU response.
* @hide
- * @deprecated Use {@link android.se.omapi.SEService} APIs instead.
+ * @deprecated Use {@link android.se.omapi.SEService} APIs instead. See
+ * {@link android.se.omapi.SEService#getUiccReader(int)},
+ * {@link android.se.omapi.Reader#openSession()},
+ * {@link android.se.omapi.Session#openBasicChannel(byte[], byte)},
+ * {@link android.se.omapi.Channel#transmit(byte[])}.
*/
- // TODO(b/147153909): Update Javadoc to link to specific SEService API once integrated.
@Deprecated
public byte[] iccExchangeSimIO(int subId, int fileID, int command, int p1, int p2,
int p3, String filePath) {
@@ -6464,9 +6462,12 @@
* @return The APDU response from the ICC card in hexadecimal format
* with the last 4 bytes being the status word. If the command fails,
* returns an empty string.
- * @deprecated Use {@link android.se.omapi.SEService} APIs instead.
+ * @deprecated Use {@link android.se.omapi.SEService} APIs instead. See
+ * {@link android.se.omapi.SEService#getUiccReader(int)},
+ * {@link android.se.omapi.Reader#openSession()},
+ * {@link android.se.omapi.Session#openBasicChannel(byte[], byte)},
+ * {@link android.se.omapi.Channel#transmit(byte[])}.
*/
- // TODO(b/147153909): Update Javadoc to link to specific SEService API once integrated.
@Deprecated
public String sendEnvelopeWithStatus(String content) {
return sendEnvelopeWithStatus(getSubId(), content);
@@ -6487,9 +6488,12 @@
* with the last 4 bytes being the status word. If the command fails,
* returns an empty string.
* @hide
- * @deprecated Use {@link android.se.omapi.SEService} APIs instead.
+ * @deprecated Use {@link android.se.omapi.SEService} APIs instead. See
+ * {@link android.se.omapi.SEService#getUiccReader(int)},
+ * {@link android.se.omapi.Reader#openSession()},
+ * {@link android.se.omapi.Session#openBasicChannel(byte[], byte)},
+ * {@link android.se.omapi.Channel#transmit(byte[])}.
*/
- // TODO(b/147153909): Update Javadoc to link to specific SEService API once integrated.
@Deprecated
public String sendEnvelopeWithStatus(int subId, String content) {
try {
@@ -10840,28 +10844,6 @@
}
/**
- * Get aggregated video call data usage since boot.
- * Permissions android.Manifest.permission.READ_NETWORK_USAGE_HISTORY is required.
- *
- * @param how one of the NetworkStats.STATS_PER_* constants depending on whether the request is
- * for data usage per uid or overall usage.
- * @return Snapshot of video call data usage
- * @hide
- */
- public NetworkStats getVtDataUsage(int how) {
- boolean perUidStats = (how == NetworkStats.STATS_PER_UID);
- try {
- ITelephony service = getITelephony();
- if (service != null) {
- return service.getVtDataUsage(getSubId(), perUidStats);
- }
- } catch (RemoteException e) {
- Log.e(TAG, "Error calling ITelephony#getVtDataUsage", e);
- }
- return null;
- }
-
- /**
* Policy control of data connection. Usually used when data limit is passed.
* @param enabled True if enabling the data, otherwise disabling.
* @param subId sub id
@@ -11623,6 +11605,7 @@
* subscription, the key is {@link SubscriptionManager#getDefaultSubscriptionId}) and the value
* as the list of {@link EmergencyNumber}; empty Map if this information is not available;
* or throw a SecurityException if the caller does not have the permission.
+ * @throws IllegalStateException if the Telephony process is not currently available.
*/
@RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
@NonNull
@@ -11670,6 +11653,7 @@
* @param number - the number to look up
* @return {@code true} if the given number is an emergency number based on current locale,
* SIM card(s), Android database, modem, network or defaults; {@code false} otherwise.
+ * @throws IllegalStateException if the Telephony process is not currently available.
*/
public boolean isEmergencyNumber(@NonNull String number) {
try {
@@ -11705,7 +11689,7 @@
* the same digits of any current emergency number based on current locale, sim, modem and
* network; {@code false} if it is not; or throw an SecurityException if the caller does not
* have the required permission/privileges
- *
+ * @throws IllegalStateException if the Telephony process is not currently available.
* @hide
*/
@SystemApi
diff --git a/telephony/java/android/telephony/ims/ImsCallSession.java b/telephony/java/android/telephony/ims/ImsCallSession.java
old mode 100644
new mode 100755
index 1b583fd..80c38cb
--- a/telephony/java/android/telephony/ims/ImsCallSession.java
+++ b/telephony/java/android/telephony/ims/ImsCallSession.java
@@ -16,6 +16,8 @@
package android.telephony.ims;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.os.Message;
import android.os.RemoteException;
import android.telephony.CallQuality;
@@ -451,6 +453,21 @@
}
/**
+ * Received success response for call transfer request.
+ */
+ public void callSessionTransferred(@NonNull ImsCallSession session) {
+ // no-op
+ }
+
+ /**
+ * Received failure response for call transfer request.
+ */
+ public void callSessionTransferFailed(@NonNull ImsCallSession session,
+ @Nullable ImsReasonInfo reasonInfo) {
+ // no-op
+ }
+
+ /**
* Called when the IMS service reports a change to the call quality.
*/
public void callQualityChanged(CallQuality callQuality) {
@@ -795,6 +812,41 @@
}
/**
+ * Transfers an ongoing call.
+ *
+ * @param number number to be transferred to.
+ * @param isConfirmationRequired indicates blind or assured transfer.
+ */
+ public void transfer(@NonNull String number, boolean isConfirmationRequired) {
+ if (mClosed) {
+ return;
+ }
+
+ try {
+ miSession.transfer(number, isConfirmationRequired);
+ } catch (RemoteException e) {
+ }
+ }
+
+ /**
+ * Transfers a call to another ongoing call.
+ *
+ * @param transferToSession the other ImsCallSession to which this session will be transferred.
+ */
+ public void transfer(@NonNull ImsCallSession transferToSession) {
+ if (mClosed) {
+ return;
+ }
+
+ try {
+ if (transferToSession != null) {
+ miSession.consultativeTransfer(transferToSession.getSession());
+ }
+ } catch (RemoteException e) {
+ }
+ }
+
+ /**
* Terminates a call.
*
* @see Listener#callSessionTerminated
@@ -1410,6 +1462,20 @@
}
}
+ @Override
+ public void callSessionTransferred() {
+ if (mListener != null) {
+ mListener.callSessionTransferred(ImsCallSession.this);
+ }
+ }
+
+ @Override
+ public void callSessionTransferFailed(@Nullable ImsReasonInfo reasonInfo) {
+ if (mListener != null) {
+ mListener.callSessionTransferFailed(ImsCallSession.this, reasonInfo);
+ }
+ }
+
/**
* Call quality updated
*/
diff --git a/telephony/java/android/telephony/ims/Rcs1To1Thread.java b/telephony/java/android/telephony/ims/Rcs1To1Thread.java
deleted file mode 100644
index e90548a..0000000
--- a/telephony/java/android/telephony/ims/Rcs1To1Thread.java
+++ /dev/null
@@ -1,92 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.telephony.ims;
-
-import android.annotation.NonNull;
-import android.annotation.WorkerThread;
-
-/**
- * Rcs1To1Thread represents a single RCS conversation thread with a total of two
- * {@link RcsParticipant}s. Please see Section 5 (1-to-1 Messaging) - GSMA RCC.71 (RCS Universal
- * Profile Service Definition Document)
- *
- * @hide
- */
-public class Rcs1To1Thread extends RcsThread {
- private int mThreadId;
-
- /**
- * Public constructor only for RcsMessageStoreController to initialize new threads.
- *
- * @hide
- */
- public Rcs1To1Thread(RcsControllerCall rcsControllerCall, int threadId) {
- super(rcsControllerCall, threadId);
- mThreadId = threadId;
- }
-
- /**
- * @return Returns {@code false} as this is always a 1 to 1 thread.
- */
- @Override
- public boolean isGroup() {
- return false;
- }
-
- /**
- * {@link Rcs1To1Thread}s can fall back to SMS as a back-up protocol. This function returns the
- * thread id to be used to query {@code content://mms-sms/conversation/#} to get the fallback
- * thread.
- *
- * @return The thread id to be used to query the mms-sms authority
- * @throws RcsMessageStoreException if the value could not be read from the storage
- */
- @WorkerThread
- public long getFallbackThreadId() throws RcsMessageStoreException {
- return mRcsControllerCall.call(
- (iRcs, callingPackage) -> iRcs.get1To1ThreadFallbackThreadId(mThreadId,
- callingPackage));
- }
-
- /**
- * If the RCS client allows falling back to SMS, it needs to create an MMS-SMS thread in the
- * SMS/MMS Provider( see {@link android.provider.Telephony.MmsSms#CONTENT_CONVERSATIONS_URI}.
- * Use this function to link the {@link Rcs1To1Thread} to the MMS-SMS thread. This function
- * also updates the storage.
- *
- * @throws RcsMessageStoreException if the value could not be persisted into storage
- */
- @WorkerThread
- public void setFallbackThreadId(long fallbackThreadId) throws RcsMessageStoreException {
- mRcsControllerCall.callWithNoReturn(
- (iRcs, callingPackage) -> iRcs.set1To1ThreadFallbackThreadId(mThreadId,
- fallbackThreadId, callingPackage));
- }
-
- /**
- * @return Returns the {@link RcsParticipant} that receives the messages sent in this thread.
- * @throws RcsMessageStoreException if the value could not be read from the storage
- */
- @NonNull
- @WorkerThread
- public RcsParticipant getRecipient() throws RcsMessageStoreException {
- return new RcsParticipant(
- mRcsControllerCall,
- mRcsControllerCall.call(
- (iRcs, callingPackage) -> iRcs.get1To1ThreadOtherParticipantId(mThreadId,
- callingPackage)));
- }
-}
diff --git a/telephony/java/android/telephony/ims/RcsControllerCall.java b/telephony/java/android/telephony/ims/RcsControllerCall.java
deleted file mode 100644
index ce03c3c..0000000
--- a/telephony/java/android/telephony/ims/RcsControllerCall.java
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.telephony.ims;
-
-import android.content.Context;
-import android.os.RemoteException;
-import android.os.ServiceManager;
-import android.telephony.ims.aidl.IRcsMessage;
-
-/**
- * A wrapper class around RPC calls that {@link RcsMessageManager} APIs to minimize boilerplate
- * code.
- *
- * @hide - not meant for public use
- */
-class RcsControllerCall {
- private final Context mContext;
-
- RcsControllerCall(Context context) {
- mContext = context;
- }
-
- <R> R call(RcsServiceCall<R> serviceCall) throws RcsMessageStoreException {
- IRcsMessage iRcsMessage = IRcsMessage.Stub.asInterface(ServiceManager.getService(
- Context.TELEPHONY_RCS_MESSAGE_SERVICE));
- if (iRcsMessage == null) {
- throw new RcsMessageStoreException("Could not connect to RCS storage service");
- }
-
- try {
- return serviceCall.methodOnIRcs(iRcsMessage, mContext.getOpPackageName());
- } catch (RemoteException exception) {
- throw new RcsMessageStoreException(exception.getMessage());
- }
- }
-
- void callWithNoReturn(RcsServiceCallWithNoReturn serviceCall)
- throws RcsMessageStoreException {
- call((iRcsMessage, callingPackage) -> {
- serviceCall.methodOnIRcs(iRcsMessage, callingPackage);
- return null;
- });
- }
-
- interface RcsServiceCall<R> {
- R methodOnIRcs(IRcsMessage iRcs, String callingPackage) throws RemoteException;
- }
-
- interface RcsServiceCallWithNoReturn {
- void methodOnIRcs(IRcsMessage iRcs, String callingPackage) throws RemoteException;
- }
-}
diff --git a/telephony/java/android/telephony/ims/RcsEvent.java b/telephony/java/android/telephony/ims/RcsEvent.java
deleted file mode 100644
index 9dd0720..0000000
--- a/telephony/java/android/telephony/ims/RcsEvent.java
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * 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 android.telephony.ims;
-
-/**
- * The base class for events that can happen on {@link RcsParticipant}s and {@link RcsThread}s.
- *
- * @hide
- */
-public abstract class RcsEvent {
- private final long mTimestamp;
-
- protected RcsEvent(long timestamp) {
- mTimestamp = timestamp;
- }
-
- /**
- * @return Returns the time of when this event happened. The timestamp is defined as
- * milliseconds passed after midnight, January 1, 1970 UTC
- */
- public long getTimestamp() {
- return mTimestamp;
- }
-
- /**
- * Persists the event to the data store
- *
- * @hide
- */
- abstract void persist(RcsControllerCall rcsControllerCall) throws RcsMessageStoreException;
-}
diff --git a/telephony/java/android/telephony/ims/RcsEventDescriptor.aidl b/telephony/java/android/telephony/ims/RcsEventDescriptor.aidl
deleted file mode 100644
index ab1c55e..0000000
--- a/telephony/java/android/telephony/ims/RcsEventDescriptor.aidl
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- *
- * Copyright 2019, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.telephony.ims;
-
-parcelable RcsEventDescriptor;
diff --git a/telephony/java/android/telephony/ims/RcsEventDescriptor.java b/telephony/java/android/telephony/ims/RcsEventDescriptor.java
deleted file mode 100644
index b44adea..0000000
--- a/telephony/java/android/telephony/ims/RcsEventDescriptor.java
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.telephony.ims;
-
-import static com.android.internal.annotations.VisibleForTesting.Visibility.PROTECTED;
-
-import android.os.Parcel;
-import android.os.Parcelable;
-
-import com.android.internal.annotations.VisibleForTesting;
-
-/**
- * @hide - used only for internal communication with the ircs service
- */
-public abstract class RcsEventDescriptor implements Parcelable {
- protected final long mTimestamp;
-
- RcsEventDescriptor(long timestamp) {
- mTimestamp = timestamp;
- }
-
- /**
- * Creates an RcsEvent based on this RcsEventDescriptor. Overriding this method practically
- * allows an injection point for RcsEvent dependencies outside of the values contained in the
- * descriptor.
- */
- @VisibleForTesting(visibility = PROTECTED)
- public abstract RcsEvent createRcsEvent(RcsControllerCall rcsControllerCall);
-
- RcsEventDescriptor(Parcel in) {
- mTimestamp = in.readLong();
- }
-
- @Override
- public void writeToParcel(Parcel dest, int flags) {
- dest.writeLong(mTimestamp);
- }
-
- @Override
- public int describeContents() {
- return 0;
- }
-}
diff --git a/telephony/java/android/telephony/ims/RcsEventQueryParams.aidl b/telephony/java/android/telephony/ims/RcsEventQueryParams.aidl
deleted file mode 100644
index f18c4df..0000000
--- a/telephony/java/android/telephony/ims/RcsEventQueryParams.aidl
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- *
- * Copyright 2019, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.telephony.ims;
-
-parcelable RcsEventQueryParams;
diff --git a/telephony/java/android/telephony/ims/RcsEventQueryParams.java b/telephony/java/android/telephony/ims/RcsEventQueryParams.java
deleted file mode 100644
index 0024cf7..0000000
--- a/telephony/java/android/telephony/ims/RcsEventQueryParams.java
+++ /dev/null
@@ -1,321 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.telephony.ims;
-
-import static android.provider.Telephony.RcsColumns.RcsEventTypes.ICON_CHANGED_EVENT_TYPE;
-import static android.provider.Telephony.RcsColumns.RcsEventTypes.NAME_CHANGED_EVENT_TYPE;
-import static android.provider.Telephony.RcsColumns.RcsEventTypes.PARTICIPANT_ALIAS_CHANGED_EVENT_TYPE;
-import static android.provider.Telephony.RcsColumns.RcsEventTypes.PARTICIPANT_JOINED_EVENT_TYPE;
-import static android.provider.Telephony.RcsColumns.RcsEventTypes.PARTICIPANT_LEFT_EVENT_TYPE;
-
-import android.annotation.CheckResult;
-import android.annotation.IntDef;
-import android.annotation.IntRange;
-import android.annotation.NonNull;
-import android.os.Parcel;
-import android.os.Parcelable;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.security.InvalidParameterException;
-
-/**
- * The parameters to pass into
- * {@link RcsMessageStore#getRcsEvents(RcsEventQueryParams)} in order to select a
- * subset of {@link RcsEvent}s present in the message store.
- *
- * @hide
- */
-public final class RcsEventQueryParams implements Parcelable {
- /**
- * Flag to be used with {@link Builder#setEventType(int)} to make
- * {@link RcsMessageStore#getRcsEvents(RcsEventQueryParams)} return all types of
- * {@link RcsEvent}s
- */
- public static final int ALL_EVENTS = -1;
-
- /**
- * Flag to be used with {@link Builder#setEventType(int)} to make
- * {@link RcsMessageStore#getRcsEvents(RcsEventQueryParams)} return sub-types of
- * {@link RcsGroupThreadEvent}s
- */
- public static final int ALL_GROUP_THREAD_EVENTS = 0;
-
- /**
- * Flag to be used with {@link Builder#setEventType(int)} to make
- * {@link RcsMessageStore#getRcsEvents(RcsEventQueryParams)} return only
- * {@link RcsParticipantAliasChangedEvent}s
- */
- public static final int PARTICIPANT_ALIAS_CHANGED_EVENT =
- PARTICIPANT_ALIAS_CHANGED_EVENT_TYPE;
-
- /**
- * Flag to be used with {@link Builder#setEventType(int)} to make
- * {@link RcsMessageStore#getRcsEvents(RcsEventQueryParams)} return only
- * {@link RcsGroupThreadParticipantJoinedEvent}s
- */
- public static final int GROUP_THREAD_PARTICIPANT_JOINED_EVENT =
- PARTICIPANT_JOINED_EVENT_TYPE;
-
- /**
- * Flag to be used with {@link Builder#setEventType(int)} to make
- * {@link RcsMessageStore#getRcsEvents(RcsEventQueryParams)} return only
- * {@link RcsGroupThreadParticipantLeftEvent}s
- */
- public static final int GROUP_THREAD_PARTICIPANT_LEFT_EVENT =
- PARTICIPANT_LEFT_EVENT_TYPE;
-
- /**
- * Flag to be used with {@link Builder#setEventType(int)} to make
- * {@link RcsMessageStore#getRcsEvents(RcsEventQueryParams)} return only
- * {@link RcsGroupThreadNameChangedEvent}s
- */
- public static final int GROUP_THREAD_NAME_CHANGED_EVENT = NAME_CHANGED_EVENT_TYPE;
-
- /**
- * Flag to be used with {@link Builder#setEventType(int)} to make
- * {@link RcsMessageStore#getRcsEvents(RcsEventQueryParams)} return only
- * {@link RcsGroupThreadIconChangedEvent}s
- */
- public static final int GROUP_THREAD_ICON_CHANGED_EVENT = ICON_CHANGED_EVENT_TYPE;
-
- @Retention(RetentionPolicy.SOURCE)
- @IntDef({ALL_EVENTS, ALL_GROUP_THREAD_EVENTS, PARTICIPANT_ALIAS_CHANGED_EVENT,
- GROUP_THREAD_PARTICIPANT_JOINED_EVENT, GROUP_THREAD_PARTICIPANT_LEFT_EVENT,
- GROUP_THREAD_NAME_CHANGED_EVENT, GROUP_THREAD_ICON_CHANGED_EVENT})
- public @interface EventType {
- }
-
- /**
- * Flag to be used with {@link Builder#setSortProperty(int)} that makes the result set sorted
- * in the order of creation for faster query results.
- */
- public static final int SORT_BY_CREATION_ORDER = 0;
-
- /**
- * Flag to be used with {@link Builder#setSortProperty(int)} that makes the result set sorted
- * with respect to {@link RcsEvent#getTimestamp()}
- */
- public static final int SORT_BY_TIMESTAMP = 1;
-
- @Retention(RetentionPolicy.SOURCE)
- @IntDef({SORT_BY_CREATION_ORDER, SORT_BY_TIMESTAMP})
- public @interface SortingProperty {
- }
-
- /**
- * The key to pass into a Bundle, for usage in RcsProvider.query(Bundle)
- * @hide - not meant for public use
- */
- public static final String EVENT_QUERY_PARAMETERS_KEY = "event_query_parameters";
-
- // Which types of events the results should be limited to
- private @EventType int mEventType;
- // The property which the results should be sorted against
- private int mSortingProperty;
- // Whether the results should be sorted in ascending order
- private boolean mIsAscending;
- // The number of results that should be returned with this query
- private int mLimit;
- // The thread that the results are limited to
- private int mThreadId;
-
- RcsEventQueryParams(@EventType int eventType, int threadId,
- @SortingProperty int sortingProperty, boolean isAscending, int limit) {
- mEventType = eventType;
- mSortingProperty = sortingProperty;
- mIsAscending = isAscending;
- mLimit = limit;
- mThreadId = threadId;
- }
-
- /**
- * @return Returns the type of {@link RcsEvent}s that this {@link RcsEventQueryParams} is
- * set to query for.
- */
- public @EventType int getEventType() {
- return mEventType;
- }
-
- /**
- * @return Returns the number of {@link RcsEvent}s to be returned from the query. A value of
- * 0 means there is no set limit.
- */
- public int getLimit() {
- return mLimit;
- }
-
- /**
- * @return Returns the property where the results should be sorted against.
- * @see SortingProperty
- */
- public int getSortingProperty() {
- return mSortingProperty;
- }
-
- /**
- * @return Returns {@code true} if the result set will be sorted in ascending order,
- * {@code false} if it will be sorted in descending order.
- */
- public boolean getSortDirection() {
- return mIsAscending;
- }
-
- /**
- * @return Returns the ID of the {@link RcsGroupThread} that the results are limited to. As this
- * API exposes an ID, it should stay hidden.
- *
- * @hide
- */
- public int getThreadId() {
- return mThreadId;
- }
-
- /**
- * A helper class to build the {@link RcsEventQueryParams}.
- */
- public static class Builder {
- private @EventType int mEventType;
- private @SortingProperty int mSortingProperty;
- private boolean mIsAscending;
- private int mLimit = 100;
- private int mThreadId;
-
- /**
- * Creates a new builder for {@link RcsEventQueryParams} to be used in
- * {@link RcsMessageStore#getRcsEvents(RcsEventQueryParams)}
- */
- public Builder() {
- // empty implementation
- }
-
- /**
- * Desired number of events to be returned from the query. Passing in 0 will return all
- * existing events at once. The limit defaults to 100.
- *
- * @param limit The number to limit the query result to.
- * @return The same instance of the builder to chain parameters.
- * @throws InvalidParameterException If the given limit is negative.
- */
- @CheckResult
- public Builder setResultLimit(@IntRange(from = 0) int limit)
- throws InvalidParameterException {
- if (limit < 0) {
- throw new InvalidParameterException("The query limit must be non-negative");
- }
-
- mLimit = limit;
- return this;
- }
-
- /**
- * Sets the type of events to be returned from the query.
- *
- * @param eventType The type of event to be returned.
- * @return The same instance of the builder to chain parameters.
- */
- @CheckResult
- public Builder setEventType(@EventType int eventType) {
- mEventType = eventType;
- return this;
- }
-
- /**
- * Sets the property where the results should be sorted against. Defaults to
- * {@link RcsEventQueryParams.SortingProperty#SORT_BY_CREATION_ORDER}
- *
- * @param sortingProperty against which property the results should be sorted
- * @return The same instance of the builder to chain parameters.
- */
- @CheckResult
- public Builder setSortProperty(@SortingProperty int sortingProperty) {
- mSortingProperty = sortingProperty;
- return this;
- }
-
- /**
- * Sets whether the results should be sorted ascending or descending
- *
- * @param isAscending whether the results should be sorted ascending
- * @return The same instance of the builder to chain parameters.
- */
- @CheckResult
- public Builder setSortDirection(boolean isAscending) {
- mIsAscending = isAscending;
- return this;
- }
-
- /**
- * Limits the results to the given {@link RcsGroupThread}. Setting this value prevents
- * returning any instances of {@link RcsParticipantAliasChangedEvent}.
- *
- * @param groupThread The thread to limit the results to.
- * @return The same instance of the builder to chain parameters.
- */
- @CheckResult
- public Builder setGroupThread(@NonNull RcsGroupThread groupThread) {
- mThreadId = groupThread.getThreadId();
- return this;
- }
-
- /**
- * Builds the {@link RcsEventQueryParams} to use in
- * {@link RcsMessageStore#getRcsEvents(RcsEventQueryParams)}
- *
- * @return An instance of {@link RcsEventQueryParams} to use with the event query.
- */
- public RcsEventQueryParams build() {
- return new RcsEventQueryParams(mEventType, mThreadId, mSortingProperty,
- mIsAscending, mLimit);
- }
- }
-
- private RcsEventQueryParams(Parcel in) {
- mEventType = in.readInt();
- mThreadId = in.readInt();
- mSortingProperty = in.readInt();
- mIsAscending = in.readBoolean();
- mLimit = in.readInt();
- }
-
- public static final @android.annotation.NonNull Creator<RcsEventQueryParams> CREATOR =
- new Creator<RcsEventQueryParams>() {
- @Override
- public RcsEventQueryParams createFromParcel(Parcel in) {
- return new RcsEventQueryParams(in);
- }
-
- @Override
- public RcsEventQueryParams[] newArray(int size) {
- return new RcsEventQueryParams[size];
- }
- };
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public void writeToParcel(Parcel dest, int flags) {
- dest.writeInt(mEventType);
- dest.writeInt(mThreadId);
- dest.writeInt(mSortingProperty);
- dest.writeBoolean(mIsAscending);
- dest.writeInt(mLimit);
- }
-}
diff --git a/telephony/java/android/telephony/ims/RcsEventQueryResult.java b/telephony/java/android/telephony/ims/RcsEventQueryResult.java
deleted file mode 100644
index d6347e3..0000000
--- a/telephony/java/android/telephony/ims/RcsEventQueryResult.java
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.telephony.ims;
-
-import java.util.List;
-
-/**
- * The result of a {@link RcsMessageStore#getRcsEvents(RcsEventQueryParams)}
- * call. This class allows getting the token for querying the next batch of events in order to
- * prevent handling large amounts of data at once.
- *
- * @hide
- */
-public class RcsEventQueryResult {
- private RcsQueryContinuationToken mContinuationToken;
- private List<RcsEvent> mEvents;
-
- /**
- * Internal constructor for {@link com.android.internal.telephony.ims.RcsMessageStoreController}
- * to create query results
- *
- * @hide
- */
- public RcsEventQueryResult(
- RcsQueryContinuationToken continuationToken,
- List<RcsEvent> events) {
- mContinuationToken = continuationToken;
- mEvents = events;
- }
-
- /**
- * Returns a token to call
- * {@link RcsMessageStore#getRcsEvents(RcsQueryContinuationToken)}
- * to get the next batch of {@link RcsEvent}s.
- */
- public RcsQueryContinuationToken getContinuationToken() {
- return mContinuationToken;
- }
-
- /**
- * Returns all the {@link RcsEvent}s in the current query result. Call {@link
- * RcsMessageStore#getRcsEvents(RcsQueryContinuationToken)} to get the next batch
- * of {@link RcsEvent}s.
- */
- public List<RcsEvent> getEvents() {
- return mEvents;
- }
-}
diff --git a/telephony/java/android/telephony/ims/RcsEventQueryResultDescriptor.aidl b/telephony/java/android/telephony/ims/RcsEventQueryResultDescriptor.aidl
deleted file mode 100644
index 0beaaab..0000000
--- a/telephony/java/android/telephony/ims/RcsEventQueryResultDescriptor.aidl
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- *
- * Copyright 2019, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.telephony.ims;
-
-parcelable RcsEventQueryResultDescriptor;
diff --git a/telephony/java/android/telephony/ims/RcsEventQueryResultDescriptor.java b/telephony/java/android/telephony/ims/RcsEventQueryResultDescriptor.java
deleted file mode 100644
index b972d55..0000000
--- a/telephony/java/android/telephony/ims/RcsEventQueryResultDescriptor.java
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.telephony.ims;
-
-import android.os.Parcel;
-import android.os.Parcelable;
-
-import java.util.LinkedList;
-import java.util.List;
-import java.util.stream.Collectors;
-
-/**
- * Contains the raw data backing a {@link RcsEventQueryResult}.
- *
- * @hide - used only for internal communication with the ircs service
- */
-public class RcsEventQueryResultDescriptor implements Parcelable {
- private final RcsQueryContinuationToken mContinuationToken;
- private final List<RcsEventDescriptor> mEvents;
-
- public RcsEventQueryResultDescriptor(
- RcsQueryContinuationToken continuationToken,
- List<RcsEventDescriptor> events) {
- mContinuationToken = continuationToken;
- mEvents = events;
- }
-
- protected RcsEventQueryResult getRcsEventQueryResult(RcsControllerCall rcsControllerCall) {
- List<RcsEvent> rcsEvents = mEvents.stream()
- .map(rcsEvent -> rcsEvent.createRcsEvent(rcsControllerCall))
- .collect(Collectors.toList());
-
- return new RcsEventQueryResult(mContinuationToken, rcsEvents);
- }
-
- protected RcsEventQueryResultDescriptor(Parcel in) {
- mContinuationToken = in.readParcelable(RcsQueryContinuationToken.class.getClassLoader());
- mEvents = new LinkedList<>();
- in.readList(mEvents, null);
- }
-
- public static final @android.annotation.NonNull Creator<RcsEventQueryResultDescriptor> CREATOR =
- new Creator<RcsEventQueryResultDescriptor>() {
- @Override
- public RcsEventQueryResultDescriptor createFromParcel(Parcel in) {
- return new RcsEventQueryResultDescriptor(in);
- }
-
- @Override
- public RcsEventQueryResultDescriptor[] newArray(int size) {
- return new RcsEventQueryResultDescriptor[size];
- }
- };
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public void writeToParcel(Parcel dest, int flags) {
- dest.writeParcelable(mContinuationToken, flags);
- dest.writeList(mEvents);
- }
-}
diff --git a/telephony/java/android/telephony/ims/RcsFileTransferCreationParams.aidl b/telephony/java/android/telephony/ims/RcsFileTransferCreationParams.aidl
deleted file mode 100644
index 1552190..0000000
--- a/telephony/java/android/telephony/ims/RcsFileTransferCreationParams.aidl
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- *
- * Copyright 2019, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.telephony.ims;
-
-parcelable RcsFileTransferCreationParams;
diff --git a/telephony/java/android/telephony/ims/RcsFileTransferCreationParams.java b/telephony/java/android/telephony/ims/RcsFileTransferCreationParams.java
deleted file mode 100644
index e43552d..0000000
--- a/telephony/java/android/telephony/ims/RcsFileTransferCreationParams.java
+++ /dev/null
@@ -1,360 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.telephony.ims;
-
-import android.annotation.CheckResult;
-import android.net.Uri;
-import android.os.Parcel;
-import android.os.Parcelable;
-
-/**
- * Pass an instance of this class to
- * {@link RcsMessage#insertFileTransfer(RcsFileTransferCreationParams)} create an
- * {@link RcsFileTransferPart} and save it into storage.
- *
- * @hide
- */
-public final class RcsFileTransferCreationParams implements Parcelable {
- private String mRcsFileTransferSessionId;
- private Uri mContentUri;
- private String mContentMimeType;
- private long mFileSize;
- private long mTransferOffset;
- private int mWidth;
- private int mHeight;
- private long mMediaDuration;
- private Uri mPreviewUri;
- private String mPreviewMimeType;
- private @RcsFileTransferPart.RcsFileTransferStatus int mFileTransferStatus;
-
- /**
- * @return Returns the globally unique RCS file transfer session ID for the
- * {@link RcsFileTransferPart} to be created
- */
- public String getRcsFileTransferSessionId() {
- return mRcsFileTransferSessionId;
- }
-
- /**
- * @return Returns the URI for the content of the {@link RcsFileTransferPart} to be created
- */
- public Uri getContentUri() {
- return mContentUri;
- }
-
- /**
- * @return Returns the MIME type for the content of the {@link RcsFileTransferPart} to be
- * created
- */
- public String getContentMimeType() {
- return mContentMimeType;
- }
-
- /**
- * @return Returns the file size in bytes for the {@link RcsFileTransferPart} to be created
- */
- public long getFileSize() {
- return mFileSize;
- }
-
- /**
- * @return Returns the transfer offset for the {@link RcsFileTransferPart} to be created. The
- * file transfer offset is defined as how many bytes have been successfully transferred to the
- * receiver of this file transfer.
- */
- public long getTransferOffset() {
- return mTransferOffset;
- }
-
- /**
- * @return Returns the width of the {@link RcsFileTransferPart} to be created. The value is in
- * pixels.
- */
- public int getWidth() {
- return mWidth;
- }
-
- /**
- * @return Returns the height of the {@link RcsFileTransferPart} to be created. The value is in
- * pixels.
- */
- public int getHeight() {
- return mHeight;
- }
-
- /**
- * @return Returns the duration of the {@link RcsFileTransferPart} to be created.
- */
- public long getMediaDuration() {
- return mMediaDuration;
- }
-
- /**
- * @return Returns the URI of the preview of the content of the {@link RcsFileTransferPart} to
- * be created. This should only be used for multi-media files.
- */
- public Uri getPreviewUri() {
- return mPreviewUri;
- }
-
- /**
- * @return Returns the MIME type of the preview of the content of the
- * {@link RcsFileTransferPart} to be created. This should only be used for multi-media files.
- */
- public String getPreviewMimeType() {
- return mPreviewMimeType;
- }
-
- /**
- * @return Returns the status of the {@link RcsFileTransferPart} to be created.
- */
- public @RcsFileTransferPart.RcsFileTransferStatus int getFileTransferStatus() {
- return mFileTransferStatus;
- }
-
- /**
- * @hide
- */
- RcsFileTransferCreationParams(Builder builder) {
- mRcsFileTransferSessionId = builder.mRcsFileTransferSessionId;
- mContentUri = builder.mContentUri;
- mContentMimeType = builder.mContentMimeType;
- mFileSize = builder.mFileSize;
- mTransferOffset = builder.mTransferOffset;
- mWidth = builder.mWidth;
- mHeight = builder.mHeight;
- mMediaDuration = builder.mLength;
- mPreviewUri = builder.mPreviewUri;
- mPreviewMimeType = builder.mPreviewMimeType;
- mFileTransferStatus = builder.mFileTransferStatus;
- }
-
- /**
- * A builder to create instances of {@link RcsFileTransferCreationParams}
- */
- public class Builder {
- private String mRcsFileTransferSessionId;
- private Uri mContentUri;
- private String mContentMimeType;
- private long mFileSize;
- private long mTransferOffset;
- private int mWidth;
- private int mHeight;
- private long mLength;
- private Uri mPreviewUri;
- private String mPreviewMimeType;
- private @RcsFileTransferPart.RcsFileTransferStatus int mFileTransferStatus;
-
- /**
- * Sets the globally unique RCS file transfer session ID for the {@link RcsFileTransferPart}
- * to be created
- *
- * @param sessionId The RCS file transfer session ID
- * @return The same instance of {@link Builder} to chain methods
- */
- @CheckResult
- public Builder setFileTransferSessionId(String sessionId) {
- mRcsFileTransferSessionId = sessionId;
- return this;
- }
-
- /**
- * Sets the URI for the content of the {@link RcsFileTransferPart} to be created
- *
- * @param contentUri The URI for the file
- * @return The same instance of {@link Builder} to chain methods
- */
- @CheckResult
- public Builder setContentUri(Uri contentUri) {
- mContentUri = contentUri;
- return this;
- }
-
- /**
- * Sets the MIME type for the content of the {@link RcsFileTransferPart} to be created
- *
- * @param contentType The MIME type of the file
- * @return The same instance of {@link Builder} to chain methods
- */
- @CheckResult
- public Builder setContentMimeType(String contentType) {
- mContentMimeType = contentType;
- return this;
- }
-
- /**
- * Sets the file size for the {@link RcsFileTransferPart} to be created
- *
- * @param size The size of the file in bytes
- * @return The same instance of {@link Builder} to chain methods
- */
- @CheckResult
- public Builder setFileSize(long size) {
- mFileSize = size;
- return this;
- }
-
- /**
- * Sets the transfer offset for the {@link RcsFileTransferPart} to be created. The file
- * transfer offset is defined as how many bytes have been successfully transferred to the
- * receiver of this file transfer.
- *
- * @param offset The transfer offset in bytes
- * @return The same instance of {@link Builder} to chain methods
- */
- @CheckResult
- public Builder setTransferOffset(long offset) {
- mTransferOffset = offset;
- return this;
- }
-
- /**
- * Sets the width of the {@link RcsFileTransferPart} to be created. This should only be used
- * for multi-media files.
- *
- * @param width The width of the multi-media file in pixels.
- * @return The same instance of {@link Builder} to chain methods
- */
- @CheckResult
- public Builder setWidth(int width) {
- mWidth = width;
- return this;
- }
-
- /**
- * Sets the height of the {@link RcsFileTransferPart} to be created. This should only be
- * used for multi-media files.
- *
- * @param height The height of the multi-media file in pixels.
- * @return The same instance of {@link Builder} to chain methods
- */
- @CheckResult
- public Builder setHeight(int height) {
- mHeight = height;
- return this;
- }
-
- /**
- * Sets the length of the {@link RcsFileTransferPart} to be created. This should only be
- * used for multi-media files such as audio or video.
- *
- * @param length The length of the multi-media file in milliseconds
- * @return The same instance of {@link Builder} to chain methods
- */
- @CheckResult
- public Builder setMediaDuration(long length) {
- mLength = length;
- return this;
- }
-
- /**
- * Sets the URI of the preview of the content of the {@link RcsFileTransferPart} to be
- * created. This should only be used for multi-media files.
- *
- * @param previewUri The URI of the preview of the file transfer
- * @return The same instance of {@link Builder} to chain methods
- */
- @CheckResult
- public Builder setPreviewUri(Uri previewUri) {
- mPreviewUri = previewUri;
- return this;
- }
-
- /**
- * Sets the MIME type of the preview of the content of the {@link RcsFileTransferPart} to
- * be created. This should only be used for multi-media files.
- *
- * @param previewType The MIME type of the preview of the file transfer
- * @return The same instance of {@link Builder} to chain methods
- */
- @CheckResult
- public Builder setPreviewMimeType(String previewType) {
- mPreviewMimeType = previewType;
- return this;
- }
-
- /**
- * Sets the status of the {@link RcsFileTransferPart} to be created.
- *
- * @param status The status of the file transfer
- * @return The same instance of {@link Builder} to chain methods
- */
- @CheckResult
- public Builder setFileTransferStatus(
- @RcsFileTransferPart.RcsFileTransferStatus int status) {
- mFileTransferStatus = status;
- return this;
- }
-
- /**
- * Creates an instance of {@link RcsFileTransferCreationParams} with the given
- * parameters.
- *
- * @return The same instance of {@link Builder} to chain methods
- * @see RcsMessage#insertFileTransfer(RcsFileTransferCreationParams)
- */
- public RcsFileTransferCreationParams build() {
- return new RcsFileTransferCreationParams(this);
- }
- }
-
- private RcsFileTransferCreationParams(Parcel in) {
- mRcsFileTransferSessionId = in.readString();
- mContentUri = in.readParcelable(Uri.class.getClassLoader());
- mContentMimeType = in.readString();
- mFileSize = in.readLong();
- mTransferOffset = in.readLong();
- mWidth = in.readInt();
- mHeight = in.readInt();
- mMediaDuration = in.readLong();
- mPreviewUri = in.readParcelable(Uri.class.getClassLoader());
- mPreviewMimeType = in.readString();
- mFileTransferStatus = in.readInt();
- }
-
- public static final @android.annotation.NonNull Creator<RcsFileTransferCreationParams> CREATOR =
- new Creator<RcsFileTransferCreationParams>() {
- @Override
- public RcsFileTransferCreationParams createFromParcel(Parcel in) {
- return new RcsFileTransferCreationParams(in);
- }
-
- @Override
- public RcsFileTransferCreationParams[] newArray(int size) {
- return new RcsFileTransferCreationParams[size];
- }
- };
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public void writeToParcel(Parcel dest, int flags) {
- dest.writeString(mRcsFileTransferSessionId);
- dest.writeParcelable(mContentUri, flags);
- dest.writeString(mContentMimeType);
- dest.writeLong(mFileSize);
- dest.writeLong(mTransferOffset);
- dest.writeInt(mWidth);
- dest.writeInt(mHeight);
- dest.writeLong(mMediaDuration);
- dest.writeParcelable(mPreviewUri, flags);
- dest.writeString(mPreviewMimeType);
- dest.writeInt(mFileTransferStatus);
- }
-}
diff --git a/telephony/java/android/telephony/ims/RcsFileTransferPart.java b/telephony/java/android/telephony/ims/RcsFileTransferPart.java
deleted file mode 100644
index ef66a76..0000000
--- a/telephony/java/android/telephony/ims/RcsFileTransferPart.java
+++ /dev/null
@@ -1,388 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.telephony.ims;
-
-import android.annotation.IntDef;
-import android.annotation.Nullable;
-import android.annotation.WorkerThread;
-import android.net.Uri;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-
-/**
- * A part of a composite {@link RcsMessage} that holds a file transfer. Please see Section 7
- * (File Transfer) - GSMA RCC.71 (RCS Universal Profile Service Definition Document)
- *
- * @hide
- */
-public class RcsFileTransferPart {
- /**
- * The status to indicate that this {@link RcsFileTransferPart} is not set yet.
- */
- public static final int NOT_SET = 0;
-
- /**
- * The status to indicate that this {@link RcsFileTransferPart} is a draft and is not in the
- * process of sending yet.
- */
- public static final int DRAFT = 1;
-
- /**
- * The status to indicate that this {@link RcsFileTransferPart} is actively being sent right
- * now.
- */
- public static final int SENDING = 2;
-
- /**
- * The status to indicate that this {@link RcsFileTransferPart} was being sent, but the user has
- * paused the sending process.
- */
- public static final int SENDING_PAUSED = 3;
-
- /**
- * The status to indicate that this {@link RcsFileTransferPart} was attempted, but failed to
- * send.
- */
- public static final int SENDING_FAILED = 4;
-
- /**
- * The status to indicate that this {@link RcsFileTransferPart} is permanently cancelled to
- * send.
- */
- public static final int SENDING_CANCELLED = 5;
-
- /**
- * The status to indicate that this {@link RcsFileTransferPart} is actively being downloaded
- * right now.
- */
- public static final int DOWNLOADING = 6;
-
- /**
- * The status to indicate that this {@link RcsFileTransferPart} was being downloaded, but the
- * user paused the downloading process.
- */
- public static final int DOWNLOADING_PAUSED = 7;
-
- /**
- * The status to indicate that this {@link RcsFileTransferPart} was attempted, but failed to
- * download.
- */
- public static final int DOWNLOADING_FAILED = 8;
-
- /**
- * The status to indicate that this {@link RcsFileTransferPart} is permanently cancelled to
- * download.
- */
- public static final int DOWNLOADING_CANCELLED = 9;
-
- /**
- * The status to indicate that this {@link RcsFileTransferPart} was successfully sent or
- * received.
- */
- public static final int SUCCEEDED = 10;
-
- @IntDef({
- DRAFT, SENDING, SENDING_PAUSED, SENDING_FAILED, SENDING_CANCELLED, DOWNLOADING,
- DOWNLOADING_PAUSED, DOWNLOADING_FAILED, DOWNLOADING_CANCELLED, SUCCEEDED
- })
- @Retention(RetentionPolicy.SOURCE)
- public @interface RcsFileTransferStatus {
- }
-
- private final RcsControllerCall mRcsControllerCall;
-
- private int mId;
-
- /**
- * @hide
- */
- RcsFileTransferPart(RcsControllerCall rcsControllerCall, int id) {
- mRcsControllerCall = rcsControllerCall;
- mId = id;
- }
-
- /**
- * @hide
- */
- public void setId(int id) {
- mId = id;
- }
-
- /**
- * @hide
- */
- public int getId() {
- return mId;
- }
-
- /**
- * Sets the RCS file transfer session ID for this file transfer and persists into storage.
- *
- * @param sessionId The session ID to be used for this file transfer.
- * @throws RcsMessageStoreException if the value could not be persisted into storage
- */
- @WorkerThread
- public void setFileTransferSessionId(String sessionId) throws RcsMessageStoreException {
- mRcsControllerCall.callWithNoReturn(
- (iRcs, callingPackage) -> iRcs.setFileTransferSessionId(mId, sessionId,
- callingPackage));
- }
-
- /**
- * @return Returns the file transfer session ID.
- * @throws RcsMessageStoreException if the value could not be read from the storage
- */
- @WorkerThread
- public String getFileTransferSessionId() throws RcsMessageStoreException {
- return mRcsControllerCall.call(
- (iRcs, callingPackage) -> iRcs.getFileTransferSessionId(mId, callingPackage));
- }
-
- /**
- * Sets the content URI for this file transfer and persists into storage. The file transfer
- * should be reachable using this URI.
- *
- * @param contentUri The URI for this file transfer.
- * @throws RcsMessageStoreException if the value could not be persisted into storage
- */
- @WorkerThread
- public void setContentUri(Uri contentUri) throws RcsMessageStoreException {
- mRcsControllerCall.callWithNoReturn(
- (iRcs, callingPackage) -> iRcs.setFileTransferContentUri(mId, contentUri,
- callingPackage));
- }
-
- /**
- * @return Returns the URI for this file transfer
- * @throws RcsMessageStoreException if the value could not be read from the storage
- */
- @Nullable
- @WorkerThread
- public Uri getContentUri() throws RcsMessageStoreException {
- return mRcsControllerCall.call(
- (iRcs, callingPackage) -> iRcs.getFileTransferContentUri(mId, callingPackage));
- }
-
- /**
- * Sets the MIME type of this file transfer and persists into storage. Whether this type
- * actually matches any known or supported types is not checked.
- *
- * @param contentMimeType The type of this file transfer.
- * @throws RcsMessageStoreException if the value could not be persisted into storage
- */
- @WorkerThread
- public void setContentMimeType(String contentMimeType) throws RcsMessageStoreException {
- mRcsControllerCall.callWithNoReturn(
- (iRcs, callingPackage) -> iRcs.setFileTransferContentType(mId, contentMimeType,
- callingPackage));
- }
-
- /**
- * @return Returns the content type of this file transfer
- * @throws RcsMessageStoreException if the value could not be read from the storage
- */
- @WorkerThread
- @Nullable
- public String getContentMimeType() throws RcsMessageStoreException {
- return mRcsControllerCall.call(
- (iRcs, callingPackage) -> iRcs.getFileTransferContentType(mId, callingPackage));
- }
-
- /**
- * Sets the content length (i.e. file size) for this file transfer and persists into storage.
- *
- * @param contentLength The content length of this file transfer
- * @throws RcsMessageStoreException if the value could not be persisted into storage
- */
- @WorkerThread
- public void setFileSize(long contentLength) throws RcsMessageStoreException {
- mRcsControllerCall.callWithNoReturn(
- (iRcs, callingPackage) -> iRcs.setFileTransferFileSize(mId, contentLength,
- callingPackage));
- }
-
- /**
- * @return Returns the content length (i.e. file size) for this file transfer.
- * @throws RcsMessageStoreException if the value could not be read from the storage
- */
- @WorkerThread
- public long getFileSize() throws RcsMessageStoreException {
- return mRcsControllerCall.call(
- (iRcs, callingPackage) -> iRcs.getFileTransferFileSize(mId, callingPackage));
- }
-
- /**
- * Sets the transfer offset for this file transfer and persists into storage. The file transfer
- * offset is defined as how many bytes have been successfully transferred to the receiver of
- * this file transfer.
- *
- * @param transferOffset The transfer offset for this file transfer.
- * @throws RcsMessageStoreException if the value could not be persisted into storage
- */
- @WorkerThread
- public void setTransferOffset(long transferOffset) throws RcsMessageStoreException {
- mRcsControllerCall.callWithNoReturn(
- (iRcs, callingPackage) -> iRcs.setFileTransferTransferOffset(mId, transferOffset,
- callingPackage));
- }
-
- /**
- * @return Returns the number of bytes that have successfully transferred.
- * @throws RcsMessageStoreException if the value could not be read from the storage
- */
- @WorkerThread
- public long getTransferOffset() throws RcsMessageStoreException {
- return mRcsControllerCall.call(
- (iRcs, callingPackage) -> iRcs.getFileTransferTransferOffset(mId, callingPackage));
- }
-
- /**
- * Sets the status for this file transfer and persists into storage.
- *
- * @param status The status of this file transfer.
- * @throws RcsMessageStoreException if the value could not be persisted into storage
- */
- @WorkerThread
- public void setFileTransferStatus(@RcsFileTransferStatus int status)
- throws RcsMessageStoreException {
- mRcsControllerCall.callWithNoReturn(
- (iRcs, callingPackage) -> iRcs.setFileTransferStatus(mId, status, callingPackage));
- }
-
- /**
- * @return Returns the status of this file transfer.
- * @throws RcsMessageStoreException if the value could not be read from the storage
- */
- @WorkerThread
- public @RcsFileTransferStatus int getFileTransferStatus() throws RcsMessageStoreException {
- return mRcsControllerCall.call(
- (iRcs, callingPackage) -> iRcs.getFileTransferStatus(mId, callingPackage));
- }
-
- /**
- * @return Returns the width of this multi-media message part in pixels.
- * @throws RcsMessageStoreException if the value could not be read from the storage
- */
- @WorkerThread
- public int getWidth() throws RcsMessageStoreException {
- return mRcsControllerCall.call(
- (iRcs, callingPackage) -> iRcs.getFileTransferWidth(mId, callingPackage));
- }
-
- /**
- * Sets the width of this RCS multi-media message part and persists into storage.
- *
- * @param width The width value in pixels
- * @throws RcsMessageStoreException if the value could not be persisted into storage
- */
- @WorkerThread
- public void setWidth(int width) throws RcsMessageStoreException {
- mRcsControllerCall.callWithNoReturn(
- (iRcs, callingPackage) -> iRcs.setFileTransferWidth(mId, width, callingPackage));
- }
-
- /**
- * @return Returns the height of this multi-media message part in pixels.
- * @throws RcsMessageStoreException if the value could not be read from the storage
- */
- @WorkerThread
- public int getHeight() throws RcsMessageStoreException {
- return mRcsControllerCall.call(
- (iRcs, callingPackage) -> iRcs.getFileTransferHeight(mId, callingPackage));
- }
-
- /**
- * Sets the height of this RCS multi-media message part and persists into storage.
- *
- * @param height The height value in pixels
- * @throws RcsMessageStoreException if the value could not be persisted into storage
- */
- @WorkerThread
- public void setHeight(int height) throws RcsMessageStoreException {
- mRcsControllerCall.callWithNoReturn(
- (iRcs, callingPackage) -> iRcs.setFileTransferHeight(mId, height, callingPackage));
- }
-
- /**
- * @return Returns the length of this multi-media file (e.g. video or audio) in milliseconds.
- * @throws RcsMessageStoreException if the value could not be read from the storage
- */
- @WorkerThread
- public long getLength() throws RcsMessageStoreException {
- return mRcsControllerCall.call(
- (iRcs, callingPackage) -> iRcs.getFileTransferLength(mId, callingPackage));
- }
-
- /**
- * Sets the length of this multi-media file (e.g. video or audio) and persists into storage.
- *
- * @param length The length of the file in milliseconds.
- * @throws RcsMessageStoreException if the value could not be persisted into storage
- */
- @WorkerThread
- public void setLength(long length) throws RcsMessageStoreException {
- mRcsControllerCall.callWithNoReturn(
- (iRcs, callingPackage) -> iRcs.setFileTransferLength(mId, length, callingPackage));
- }
-
- /**
- * @return Returns the URI for the preview of this multi-media file (e.g. an image thumbnail for
- * a video)
- * @throws RcsMessageStoreException if the value could not be read from the storage
- */
- @WorkerThread
- public Uri getPreviewUri() throws RcsMessageStoreException {
- return mRcsControllerCall.call(
- (iRcs, callingPackage) -> iRcs.getFileTransferPreviewUri(mId, callingPackage));
- }
-
- /**
- * Sets the URI for the preview of this multi-media file and persists into storage.
- *
- * @param previewUri The URI to access to the preview file.
- * @throws RcsMessageStoreException if the value could not be persisted into storage
- */
- @WorkerThread
- public void setPreviewUri(Uri previewUri) throws RcsMessageStoreException {
- mRcsControllerCall.callWithNoReturn(
- (iRcs, callingPackage) -> iRcs.setFileTransferPreviewUri(mId, previewUri,
- callingPackage));
- }
-
- /**
- * @return Returns the MIME type of this multi-media file's preview.
- * @throws RcsMessageStoreException if the value could not be read from the storage
- */
- @WorkerThread
- public String getPreviewMimeType() throws RcsMessageStoreException {
- return mRcsControllerCall.call(
- (iRcs, callingPackage) -> iRcs.getFileTransferPreviewType(mId, callingPackage));
- }
-
- /**
- * Sets the MIME type for this multi-media file's preview and persists into storage.
- *
- * @param previewMimeType The MIME type for the preview
- * @throws RcsMessageStoreException if the value could not be persisted into storage
- */
- @WorkerThread
- public void setPreviewMimeType(String previewMimeType) throws RcsMessageStoreException {
- mRcsControllerCall.callWithNoReturn(
- (iRcs, callingPackage) -> iRcs.setFileTransferPreviewType(mId, previewMimeType,
- callingPackage));
- }
-}
diff --git a/telephony/java/android/telephony/ims/RcsGroupThread.java b/telephony/java/android/telephony/ims/RcsGroupThread.java
deleted file mode 100644
index 30abcb4..0000000
--- a/telephony/java/android/telephony/ims/RcsGroupThread.java
+++ /dev/null
@@ -1,223 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.telephony.ims;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.annotation.WorkerThread;
-import android.net.Uri;
-
-import java.util.Collections;
-import java.util.LinkedHashSet;
-import java.util.List;
-import java.util.Set;
-
-/**
- * RcsGroupThread represents a single RCS conversation thread where {@link RcsParticipant}s can join
- * or leave. Please see Section 6 (Group Chat) - GSMA RCC.71 (RCS Universal Profile Service
- * Definition Document)
- *
- * @hide
- */
-public class RcsGroupThread extends RcsThread {
- /**
- * Public constructor only for RcsMessageStoreController to initialize new threads.
- *
- * @hide
- */
- public RcsGroupThread(RcsControllerCall rcsControllerCall, int threadId) {
- super(rcsControllerCall, threadId);
- }
-
- /**
- * @return Returns {@code true} as this is always a group thread
- */
- @Override
- public boolean isGroup() {
- return true;
- }
-
- /**
- * @return Returns the given name of this {@link RcsGroupThread}. Please see US6-2 - GSMA RCC.71
- * (RCS Universal Profile Service Definition Document)
- * @throws RcsMessageStoreException if the value could not be read from the storage
- */
- @Nullable
- @WorkerThread
- public String getGroupName() throws RcsMessageStoreException {
- return mRcsControllerCall.call(
- (iRcs, callingPackage) -> iRcs.getGroupThreadName(mThreadId, callingPackage));
- }
-
- /**
- * Sets the name of this {@link RcsGroupThread} and saves it into storage. Please see US6-2 -
- * GSMA RCC.71 (RCS Universal Profile Service Definition Document)
- *
- * @throws RcsMessageStoreException if the value could not be persisted into storage
- */
- @WorkerThread
- public void setGroupName(String groupName) throws RcsMessageStoreException {
- mRcsControllerCall.callWithNoReturn(
- (iRcs, callingPackage) -> iRcs.setGroupThreadName(mThreadId, groupName,
- callingPackage));
- }
-
- /**
- * @return Returns a URI that points to the group's icon {@link RcsGroupThread}. Please see
- * US6-2 - GSMA RCC.71 (RCS Universal Profile Service Definition Document)
- * @throws RcsMessageStoreException if the value could not be read from the storage
- */
- @Nullable
- public Uri getGroupIcon() throws RcsMessageStoreException {
- return mRcsControllerCall.call(
- (iRcs, callingPackage) -> iRcs.getGroupThreadIcon(mThreadId, callingPackage));
- }
-
- /**
- * Sets the icon for this {@link RcsGroupThread} and saves it into storage. Please see US6-2 -
- * GSMA RCC.71 (RCS Universal Profile Service Definition Document)
- *
- * @throws RcsMessageStoreException if the value could not be persisted into storage
- */
- @WorkerThread
- public void setGroupIcon(@Nullable Uri groupIcon) throws RcsMessageStoreException {
- mRcsControllerCall.callWithNoReturn(
- (iRcs, callingPackage) -> iRcs.setGroupThreadIcon(mThreadId, groupIcon,
- callingPackage));
- }
-
- /**
- * @return Returns the owner of this thread or {@code null} if there doesn't exist an owner
- * @throws RcsMessageStoreException if the value could not be read from the storage
- */
- @Nullable
- @WorkerThread
- public RcsParticipant getOwner() throws RcsMessageStoreException {
- return new RcsParticipant(
- mRcsControllerCall,
- mRcsControllerCall.call(
- (iRcs, callingPackage) -> iRcs.getGroupThreadOwner(mThreadId,
- callingPackage)));
- }
-
- /**
- * Sets the owner of this {@link RcsGroupThread} and saves it into storage. This is intended to
- * be used for selecting a new owner for a group thread if the owner leaves the thread. The
- * owner needs to be in the list of existing participants.
- *
- * @param participant The new owner of the thread. {@code null} values are allowed.
- * @throws RcsMessageStoreException if the operation could not be persisted into storage
- */
- @WorkerThread
- public void setOwner(@Nullable RcsParticipant participant) throws RcsMessageStoreException {
- mRcsControllerCall.callWithNoReturn(
- (iRcs, callingPackage) -> iRcs.setGroupThreadOwner(mThreadId, participant.getId(),
- callingPackage));
- }
-
- /**
- * Adds a new {@link RcsParticipant} to this group thread and persists into storage. If the user
- * is actively participating in this {@link RcsGroupThread}, an {@link RcsParticipant} on behalf
- * of them should be added.
- *
- * @param participant The new participant to be added to the thread.
- * @throws RcsMessageStoreException if the operation could not be persisted into storage
- */
- @WorkerThread
- public void addParticipant(@NonNull RcsParticipant participant)
- throws RcsMessageStoreException {
- if (participant == null) {
- return;
- }
-
- mRcsControllerCall.callWithNoReturn(
- (iRcs, callingPackage) -> iRcs.addParticipantToGroupThread(mThreadId,
- participant.getId(), callingPackage));
- }
-
- /**
- * Removes an {@link RcsParticipant} from this group thread and persists into storage. If the
- * removed participant was the owner of this group, the owner will become null.
- *
- * @throws RcsMessageStoreException if the operation could not be persisted into storage
- */
- @WorkerThread
- public void removeParticipant(@NonNull RcsParticipant participant)
- throws RcsMessageStoreException {
- if (participant == null) {
- return;
- }
-
- mRcsControllerCall.callWithNoReturn(
- (iRcs, callingPackage) -> iRcs.removeParticipantFromGroupThread(mThreadId,
- participant.getId(), callingPackage));
- }
-
- /**
- * Returns the set of {@link RcsParticipant}s that contribute to this group thread. The
- * returned set does not support modifications, please use
- * {@link RcsGroupThread#addParticipant(RcsParticipant)}
- * and {@link RcsGroupThread#removeParticipant(RcsParticipant)} instead.
- *
- * @return the immutable set of {@link RcsParticipant} in this group thread.
- * @throws RcsMessageStoreException if the values could not be read from the storage
- */
- @WorkerThread
- @NonNull
- public Set<RcsParticipant> getParticipants() throws RcsMessageStoreException {
- RcsParticipantQueryParams queryParameters =
- new RcsParticipantQueryParams.Builder().setThread(this).build();
-
- RcsParticipantQueryResult queryResult = new RcsParticipantQueryResult(
- mRcsControllerCall,
- mRcsControllerCall.call(
- (iRcs, callingPackage) -> iRcs.getParticipants(queryParameters,
- callingPackage)));
-
- List<RcsParticipant> participantList = queryResult.getParticipants();
- Set<RcsParticipant> participantSet = new LinkedHashSet<>(participantList);
- return Collections.unmodifiableSet(participantSet);
- }
-
- /**
- * Returns the conference URI for this {@link RcsGroupThread}. Please see 4.4.5.2 - GSMA RCC.53
- * (RCS Device API 1.6 Specification
- *
- * @throws RcsMessageStoreException if the value could not be read from the storage
- */
- @Nullable
- @WorkerThread
- public Uri getConferenceUri() throws RcsMessageStoreException {
- return mRcsControllerCall.call(
- (iRcs, callingPackage) -> iRcs.getGroupThreadConferenceUri(mThreadId,
- callingPackage));
- }
-
- /**
- * Sets the conference URI for this {@link RcsGroupThread} and persists into storage. Please see
- * 4.4.5.2 - GSMA RCC.53 (RCS Device API 1.6 Specification
- *
- * @param conferenceUri The URI as String to be used as the conference URI.
- * @throws RcsMessageStoreException if the value could not be persisted into storage
- */
- @Nullable
- @WorkerThread
- public void setConferenceUri(Uri conferenceUri) throws RcsMessageStoreException {
- mRcsControllerCall.callWithNoReturn(
- (iRcs, callingPackage) -> iRcs.setGroupThreadConferenceUri(mThreadId, conferenceUri,
- callingPackage));
- }
-}
diff --git a/telephony/java/android/telephony/ims/RcsGroupThreadEvent.java b/telephony/java/android/telephony/ims/RcsGroupThreadEvent.java
deleted file mode 100644
index f4beef7f..0000000
--- a/telephony/java/android/telephony/ims/RcsGroupThreadEvent.java
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * 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 android.telephony.ims;
-
-import android.annotation.NonNull;
-
-/**
- * An event that happened on an {@link RcsGroupThread}.
- *
- * @hide
- */
-public abstract class RcsGroupThreadEvent extends RcsEvent {
- private final RcsGroupThread mRcsGroupThread;
- private final RcsParticipant mOriginatingParticipant;
-
- RcsGroupThreadEvent(long timestamp, RcsGroupThread rcsGroupThread,
- RcsParticipant originatingParticipant) {
- super(timestamp);
- mRcsGroupThread = rcsGroupThread;
- mOriginatingParticipant = originatingParticipant;
- }
-
- /**
- * @return Returns the {@link RcsGroupThread} that this event happened on.
- */
- @NonNull
- public RcsGroupThread getRcsGroupThread() {
- return mRcsGroupThread;
- }
-
- /**
- * @return Returns the {@link RcsParticipant} that performed the event.
- */
- @NonNull
- public RcsParticipant getOriginatingParticipant() {
- return mOriginatingParticipant;
- }
-}
diff --git a/telephony/java/android/telephony/ims/RcsGroupThreadEventDescriptor.aidl b/telephony/java/android/telephony/ims/RcsGroupThreadEventDescriptor.aidl
deleted file mode 100644
index 6299d8a..0000000
--- a/telephony/java/android/telephony/ims/RcsGroupThreadEventDescriptor.aidl
+++ /dev/null
@@ -1,19 +0,0 @@
-/*
- * Copyright 2019, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.telephony.ims;
-
-parcelable RcsGroupThreadEventDescriptor;
diff --git a/telephony/java/android/telephony/ims/RcsGroupThreadEventDescriptor.java b/telephony/java/android/telephony/ims/RcsGroupThreadEventDescriptor.java
deleted file mode 100644
index 662a264..0000000
--- a/telephony/java/android/telephony/ims/RcsGroupThreadEventDescriptor.java
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.telephony.ims;
-
-import android.os.Parcel;
-
-/**
- * @hide - used only for internal communication with the ircs service
- */
-public abstract class RcsGroupThreadEventDescriptor extends RcsEventDescriptor {
- protected final int mRcsGroupThreadId;
- protected final int mOriginatingParticipantId;
-
- RcsGroupThreadEventDescriptor(long timestamp, int rcsGroupThreadId,
- int originatingParticipantId) {
- super(timestamp);
- mRcsGroupThreadId = rcsGroupThreadId;
- mOriginatingParticipantId = originatingParticipantId;
- }
-
- RcsGroupThreadEventDescriptor(Parcel in) {
- super(in);
- mRcsGroupThreadId = in.readInt();
- mOriginatingParticipantId = in.readInt();
- }
-
- @Override
- public void writeToParcel(Parcel dest, int flags) {
- super.writeToParcel(dest, flags);
- dest.writeInt(mRcsGroupThreadId);
- dest.writeInt(mOriginatingParticipantId);
- }
-}
diff --git a/telephony/java/android/telephony/ims/RcsGroupThreadIconChangedEvent.java b/telephony/java/android/telephony/ims/RcsGroupThreadIconChangedEvent.java
deleted file mode 100644
index 23e39ff..0000000
--- a/telephony/java/android/telephony/ims/RcsGroupThreadIconChangedEvent.java
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * 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 android.telephony.ims;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.net.Uri;
-
-/**
- * An event that indicates an {@link RcsGroupThread}'s icon was changed. Please see R6-2-5 - GSMA
- * RCC.71 (RCS Universal Profile Service Definition Document)
- *
- * @hide
- */
-public final class RcsGroupThreadIconChangedEvent extends RcsGroupThreadEvent {
- private final Uri mNewIcon;
-
- /**
- * Creates a new {@link RcsGroupThreadIconChangedEvent}. This event is not persisted into
- * storage until {@link RcsMessageStore#persistRcsEvent(RcsEvent)} is called.
- *
- * @param timestamp The timestamp of when this event happened, in milliseconds passed after
- * midnight, January 1st, 1970 UTC
- * @param rcsGroupThread The {@link RcsGroupThread} that this event happened on
- * @param originatingParticipant The {@link RcsParticipant} that changed the
- * {@link RcsGroupThread}'s icon.
- * @param newIcon {@link Uri} to the new icon of this {@link RcsGroupThread}
- * @see RcsMessageStore#persistRcsEvent(RcsEvent)
- */
- public RcsGroupThreadIconChangedEvent(long timestamp,
- @NonNull RcsGroupThread rcsGroupThread, @NonNull RcsParticipant originatingParticipant,
- @Nullable Uri newIcon) {
- super(timestamp, rcsGroupThread, originatingParticipant);
- mNewIcon = newIcon;
- }
-
- /**
- * @return Returns the {@link Uri} to the icon of the {@link RcsGroupThread} after this
- * {@link RcsGroupThreadIconChangedEvent} occured.
- */
- @Nullable
- public Uri getNewIcon() {
- return mNewIcon;
- }
-
- /**
- * Persists the event to the data store.
- *
- * @hide - not meant for public use.
- */
- @Override
- void persist(RcsControllerCall rcsControllerCall) throws RcsMessageStoreException {
- // TODO ensure failure throws
- rcsControllerCall.call((iRcs, callingPackage) -> iRcs.createGroupThreadIconChangedEvent(
- getTimestamp(), getRcsGroupThread().getThreadId(),
- getOriginatingParticipant().getId(), mNewIcon, callingPackage));
- }
-}
diff --git a/telephony/java/android/telephony/ims/RcsGroupThreadIconChangedEventDescriptor.aidl b/telephony/java/android/telephony/ims/RcsGroupThreadIconChangedEventDescriptor.aidl
deleted file mode 100644
index 4bcc5a04..0000000
--- a/telephony/java/android/telephony/ims/RcsGroupThreadIconChangedEventDescriptor.aidl
+++ /dev/null
@@ -1,19 +0,0 @@
-/*
- * Copyright 2019, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.telephony.ims;
-
-parcelable RcsGroupThreadIconChangedEventDescriptor;
diff --git a/telephony/java/android/telephony/ims/RcsGroupThreadIconChangedEventDescriptor.java b/telephony/java/android/telephony/ims/RcsGroupThreadIconChangedEventDescriptor.java
deleted file mode 100644
index 9350e40..0000000
--- a/telephony/java/android/telephony/ims/RcsGroupThreadIconChangedEventDescriptor.java
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.telephony.ims;
-
-import static com.android.internal.annotations.VisibleForTesting.Visibility.PROTECTED;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.net.Uri;
-import android.os.Parcel;
-
-import com.android.internal.annotations.VisibleForTesting;
-
-/**
- * @hide - used only for internal communication with the ircs service
- */
-public class RcsGroupThreadIconChangedEventDescriptor extends RcsGroupThreadEventDescriptor {
- private final Uri mNewIcon;
-
- public RcsGroupThreadIconChangedEventDescriptor(long timestamp, int rcsGroupThreadId,
- int originatingParticipantId, @Nullable Uri newIcon) {
- super(timestamp, rcsGroupThreadId, originatingParticipantId);
- mNewIcon = newIcon;
- }
-
- @Override
- @VisibleForTesting(visibility = PROTECTED)
- public RcsGroupThreadIconChangedEvent createRcsEvent(RcsControllerCall rcsControllerCall) {
- return new RcsGroupThreadIconChangedEvent(mTimestamp,
- new RcsGroupThread(rcsControllerCall, mRcsGroupThreadId),
- new RcsParticipant(rcsControllerCall, mOriginatingParticipantId), mNewIcon);
- }
-
- public static final @NonNull Creator<RcsGroupThreadIconChangedEventDescriptor> CREATOR =
- new Creator<RcsGroupThreadIconChangedEventDescriptor>() {
- @Override
- public RcsGroupThreadIconChangedEventDescriptor createFromParcel(Parcel in) {
- return new RcsGroupThreadIconChangedEventDescriptor(in);
- }
-
- @Override
- public RcsGroupThreadIconChangedEventDescriptor[] newArray(int size) {
- return new RcsGroupThreadIconChangedEventDescriptor[size];
- }
- };
-
- protected RcsGroupThreadIconChangedEventDescriptor(Parcel in) {
- super(in);
- mNewIcon = in.readParcelable(Uri.class.getClassLoader());
- }
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public void writeToParcel(Parcel dest, int flags) {
- super.writeToParcel(dest, flags);
- dest.writeParcelable(mNewIcon, flags);
- }
-}
diff --git a/telephony/java/android/telephony/ims/RcsGroupThreadNameChangedEvent.java b/telephony/java/android/telephony/ims/RcsGroupThreadNameChangedEvent.java
deleted file mode 100644
index a6a0867..0000000
--- a/telephony/java/android/telephony/ims/RcsGroupThreadNameChangedEvent.java
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * 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 android.telephony.ims;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-
-/**
- * An event that indicates an {@link RcsGroupThread}'s name was changed. Please see R6-2-5 - GSMA
- * RCC.71 (RCS Universal Profile Service Definition Document)
- *
- * @hide
- */
-public final class RcsGroupThreadNameChangedEvent extends RcsGroupThreadEvent {
- private final String mNewName;
-
- /**
- * Creates a new {@link RcsGroupThreadNameChangedEvent}. This event is not persisted into
- * storage until {@link RcsMessageStore#persistRcsEvent(RcsEvent)} is called.
- *
- * @param timestamp The timestamp of when this event happened, in milliseconds passed after
- * midnight, January 1st, 1970 UTC
- * @param rcsGroupThread The {@link RcsGroupThread} that this event happened on
- * @param originatingParticipant The {@link RcsParticipant} that changed the
- * {@link RcsGroupThread}'s icon.
- * @param newName The new name of the {@link RcsGroupThread}
- * @see RcsMessageStore#persistRcsEvent(RcsEvent)
- */
- public RcsGroupThreadNameChangedEvent(long timestamp, @NonNull RcsGroupThread rcsGroupThread,
- @NonNull RcsParticipant originatingParticipant, @Nullable String newName) {
- super(timestamp, rcsGroupThread, originatingParticipant);
- mNewName = newName;
- }
-
- /**
- * @return Returns the name of this {@link RcsGroupThread} after this
- * {@link RcsGroupThreadNameChangedEvent} happened.
- */
- @Nullable
- public String getNewName() {
- return mNewName;
- }
-
- /**
- * Persists the event to the data store.
- *
- * @hide - not meant for public use.
- */
- @Override
- void persist(RcsControllerCall rcsControllerCall) throws RcsMessageStoreException {
- rcsControllerCall.call((iRcs, callingPackage) -> iRcs.createGroupThreadNameChangedEvent(
- getTimestamp(), getRcsGroupThread().getThreadId(),
- getOriginatingParticipant().getId(), mNewName, callingPackage));
- }
-}
diff --git a/telephony/java/android/telephony/ims/RcsGroupThreadNameChangedEventDescriptor.aidl b/telephony/java/android/telephony/ims/RcsGroupThreadNameChangedEventDescriptor.aidl
deleted file mode 100644
index 480e86b..0000000
--- a/telephony/java/android/telephony/ims/RcsGroupThreadNameChangedEventDescriptor.aidl
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- *
- * Copyright 2019, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.telephony.ims;
-
-parcelable RcsGroupThreadNameChangedEventDescriptor;
diff --git a/telephony/java/android/telephony/ims/RcsGroupThreadNameChangedEventDescriptor.java b/telephony/java/android/telephony/ims/RcsGroupThreadNameChangedEventDescriptor.java
deleted file mode 100644
index f9ccdd5..0000000
--- a/telephony/java/android/telephony/ims/RcsGroupThreadNameChangedEventDescriptor.java
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.telephony.ims;
-
-import static com.android.internal.annotations.VisibleForTesting.Visibility.PROTECTED;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.os.Parcel;
-
-import com.android.internal.annotations.VisibleForTesting;
-
-/**
- * @hide - used only for internal communication with the ircs service
- */
-public class RcsGroupThreadNameChangedEventDescriptor extends RcsGroupThreadEventDescriptor {
- private final String mNewName;
-
- public RcsGroupThreadNameChangedEventDescriptor(long timestamp, int rcsGroupThreadId,
- int originatingParticipantId, @Nullable String newName) {
- super(timestamp, rcsGroupThreadId, originatingParticipantId);
- mNewName = newName;
- }
-
- @Override
- @VisibleForTesting(visibility = PROTECTED)
- public RcsGroupThreadNameChangedEvent createRcsEvent(RcsControllerCall rcsControllerCall) {
- return new RcsGroupThreadNameChangedEvent(
- mTimestamp,
- new RcsGroupThread(rcsControllerCall, mRcsGroupThreadId),
- new RcsParticipant(rcsControllerCall, mOriginatingParticipantId),
- mNewName);
- }
-
- public static final @NonNull Creator<RcsGroupThreadNameChangedEventDescriptor> CREATOR =
- new Creator<RcsGroupThreadNameChangedEventDescriptor>() {
- @Override
- public RcsGroupThreadNameChangedEventDescriptor createFromParcel(Parcel in) {
- return new RcsGroupThreadNameChangedEventDescriptor(in);
- }
-
- @Override
- public RcsGroupThreadNameChangedEventDescriptor[] newArray(int size) {
- return new RcsGroupThreadNameChangedEventDescriptor[size];
- }
- };
-
- protected RcsGroupThreadNameChangedEventDescriptor(Parcel in) {
- super(in);
- mNewName = in.readString();
- }
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public void writeToParcel(Parcel dest, int flags) {
- super.writeToParcel(dest, flags);
- dest.writeString(mNewName);
- }
-}
diff --git a/telephony/java/android/telephony/ims/RcsGroupThreadParticipantJoinedEvent.java b/telephony/java/android/telephony/ims/RcsGroupThreadParticipantJoinedEvent.java
deleted file mode 100644
index 694c7de..0000000
--- a/telephony/java/android/telephony/ims/RcsGroupThreadParticipantJoinedEvent.java
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * 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 android.telephony.ims;
-
-import android.annotation.NonNull;
-
-/**
- * An event that indicates an RCS participant has joined an {@link RcsThread}. Please see US6-3 -
- * GSMA RCC.71 (RCS Universal Profile Service Definition Document)
- *
- * @hide
- */
-public final class RcsGroupThreadParticipantJoinedEvent extends RcsGroupThreadEvent {
- private final RcsParticipant mJoinedParticipantId;
-
- /**
- * Creates a new {@link RcsGroupThreadParticipantJoinedEvent}. This event is not persisted into
- * storage until {@link RcsMessageStore#persistRcsEvent(RcsEvent)} is called.
- *
- * @param timestamp The timestamp of when this event happened, in milliseconds
- * passed after
- * midnight, January 1st, 1970 UTC
- * @param rcsGroupThread The {@link RcsGroupThread} that this event happened on
- * @param originatingParticipant The {@link RcsParticipant} that added or invited the new
- * {@link RcsParticipant} into the {@link RcsGroupThread}
- * @param joinedParticipant The new {@link RcsParticipant} that joined the
- * {@link RcsGroupThread}
- * @see RcsMessageStore#persistRcsEvent(RcsEvent)
- */
- public RcsGroupThreadParticipantJoinedEvent(long timestamp,
- @NonNull RcsGroupThread rcsGroupThread, @NonNull RcsParticipant originatingParticipant,
- @NonNull RcsParticipant joinedParticipant) {
- super(timestamp, rcsGroupThread, originatingParticipant);
- mJoinedParticipantId = joinedParticipant;
- }
-
- /**
- * @return Returns the {@link RcsParticipant} that joined the associated {@link RcsGroupThread}
- */
- public RcsParticipant getJoinedParticipant() {
- return mJoinedParticipantId;
- }
-
- /**
- * Persists the event to the data store.
- *
- * @hide - not meant for public use.
- */
- @Override
- void persist(RcsControllerCall rcsControllerCall) throws RcsMessageStoreException {
- rcsControllerCall.call(
- (iRcs, callingPackage) -> iRcs.createGroupThreadParticipantJoinedEvent(
- getTimestamp(),
- getRcsGroupThread().getThreadId(), getOriginatingParticipant().getId(),
- getJoinedParticipant().getId(), callingPackage));
- }
-}
diff --git a/telephony/java/android/telephony/ims/RcsGroupThreadParticipantJoinedEventDescriptor.aidl b/telephony/java/android/telephony/ims/RcsGroupThreadParticipantJoinedEventDescriptor.aidl
deleted file mode 100644
index 7210b9f..0000000
--- a/telephony/java/android/telephony/ims/RcsGroupThreadParticipantJoinedEventDescriptor.aidl
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- *
- * Copyright 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 android.telephony.ims;
-
-parcelable RcsGroupThreadParticipantJoinedEventDescriptor;
diff --git a/telephony/java/android/telephony/ims/RcsGroupThreadParticipantJoinedEventDescriptor.java b/telephony/java/android/telephony/ims/RcsGroupThreadParticipantJoinedEventDescriptor.java
deleted file mode 100644
index 4a6803e..0000000
--- a/telephony/java/android/telephony/ims/RcsGroupThreadParticipantJoinedEventDescriptor.java
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.telephony.ims;
-
-import static com.android.internal.annotations.VisibleForTesting.Visibility.PROTECTED;
-
-import android.annotation.NonNull;
-import android.os.Parcel;
-
-import com.android.internal.annotations.VisibleForTesting;
-
-/**
- * @hide - used only for internal communication with the ircs service
- */
-public class RcsGroupThreadParticipantJoinedEventDescriptor extends RcsGroupThreadEventDescriptor {
- private final int mJoinedParticipantId;
-
- public RcsGroupThreadParticipantJoinedEventDescriptor(long timestamp, int rcsGroupThreadId,
- int originatingParticipantId, int joinedParticipantId) {
- super(timestamp, rcsGroupThreadId, originatingParticipantId);
- mJoinedParticipantId = joinedParticipantId;
- }
-
- @Override
- @VisibleForTesting(visibility = PROTECTED)
- public RcsGroupThreadParticipantJoinedEvent createRcsEvent(
- RcsControllerCall rcsControllerCall) {
- return new RcsGroupThreadParticipantJoinedEvent(
- mTimestamp,
- new RcsGroupThread(rcsControllerCall, mRcsGroupThreadId),
- new RcsParticipant(rcsControllerCall, mOriginatingParticipantId),
- new RcsParticipant(rcsControllerCall, mJoinedParticipantId));
- }
-
- public static final @NonNull Creator<RcsGroupThreadParticipantJoinedEventDescriptor> CREATOR =
- new Creator<RcsGroupThreadParticipantJoinedEventDescriptor>() {
- @Override
- public RcsGroupThreadParticipantJoinedEventDescriptor createFromParcel(Parcel in) {
- return new RcsGroupThreadParticipantJoinedEventDescriptor(in);
- }
-
- @Override
- public RcsGroupThreadParticipantJoinedEventDescriptor[] newArray(int size) {
- return new RcsGroupThreadParticipantJoinedEventDescriptor[size];
- }
- };
-
- protected RcsGroupThreadParticipantJoinedEventDescriptor(Parcel in) {
- super(in);
- mJoinedParticipantId = in.readInt();
- }
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public void writeToParcel(Parcel dest, int flags) {
- super.writeToParcel(dest, flags);
- dest.writeInt(mJoinedParticipantId);
- }
-}
diff --git a/telephony/java/android/telephony/ims/RcsGroupThreadParticipantLeftEvent.java b/telephony/java/android/telephony/ims/RcsGroupThreadParticipantLeftEvent.java
deleted file mode 100644
index fec4354..0000000
--- a/telephony/java/android/telephony/ims/RcsGroupThreadParticipantLeftEvent.java
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * 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 android.telephony.ims;
-
-import android.annotation.NonNull;
-
-/**
- * An event that indicates an RCS participant has left an {@link RcsThread}. Please see US6-23 -
- * GSMA RCC.71 (RCS Universal Profile Service Definition Document)
- *
- * @hide
- */
-public final class RcsGroupThreadParticipantLeftEvent extends RcsGroupThreadEvent {
- private RcsParticipant mLeavingParticipant;
-
- /**
- * Creates a new {@link RcsGroupThreadParticipantLeftEvent}. his event is not persisted into
- * storage until {@link RcsMessageStore#persistRcsEvent(RcsEvent)} is called.
- *
- * @param timestamp The timestamp of when this event happened, in milliseconds passed after
- * midnight, January 1st, 1970 UTC
- * @param rcsGroupThread The {@link RcsGroupThread} that this event happened on
- * @param originatingParticipant The {@link RcsParticipant} that removed the
- * {@link RcsParticipant} from the {@link RcsGroupThread}. It is
- * possible that originatingParticipant and leavingParticipant are
- * the same (i.e. {@link RcsParticipant} left the group
- * themselves)
- * @param leavingParticipant The {@link RcsParticipant} that left the {@link RcsGroupThread}
- * @see RcsMessageStore#persistRcsEvent(RcsEvent)
- */
- public RcsGroupThreadParticipantLeftEvent(long timestamp,
- @NonNull RcsGroupThread rcsGroupThread, @NonNull RcsParticipant originatingParticipant,
- @NonNull RcsParticipant leavingParticipant) {
- super(timestamp, rcsGroupThread, originatingParticipant);
- mLeavingParticipant = leavingParticipant;
- }
-
- /**
- * @return Returns the {@link RcsParticipant} that left the associated {@link RcsGroupThread}
- * after this {@link RcsGroupThreadParticipantLeftEvent} happened.
- */
- @NonNull
- public RcsParticipant getLeavingParticipant() {
- return mLeavingParticipant;
- }
-
- @Override
- void persist(RcsControllerCall rcsControllerCall) throws RcsMessageStoreException {
- rcsControllerCall.call(
- (iRcs, callingPackage) -> iRcs.createGroupThreadParticipantLeftEvent(getTimestamp(),
- getRcsGroupThread().getThreadId(), getOriginatingParticipant().getId(),
- getLeavingParticipant().getId(), callingPackage));
- }
-}
diff --git a/telephony/java/android/telephony/ims/RcsGroupThreadParticipantLeftEventDescriptor.aidl b/telephony/java/android/telephony/ims/RcsGroupThreadParticipantLeftEventDescriptor.aidl
deleted file mode 100644
index 3ef92100..0000000
--- a/telephony/java/android/telephony/ims/RcsGroupThreadParticipantLeftEventDescriptor.aidl
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- *
- * Copyright 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 android.telephony.ims;
-
-parcelable RcsGroupThreadParticipantLeftEventDescriptor;
diff --git a/telephony/java/android/telephony/ims/RcsGroupThreadParticipantLeftEventDescriptor.java b/telephony/java/android/telephony/ims/RcsGroupThreadParticipantLeftEventDescriptor.java
deleted file mode 100644
index 9b1085c..0000000
--- a/telephony/java/android/telephony/ims/RcsGroupThreadParticipantLeftEventDescriptor.java
+++ /dev/null
@@ -1,77 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.telephony.ims;
-
-import static com.android.internal.annotations.VisibleForTesting.Visibility.PROTECTED;
-
-import android.annotation.NonNull;
-import android.os.Parcel;
-import android.os.Parcelable;
-
-import com.android.internal.annotations.VisibleForTesting;
-
-/**
- * @hide - used only for internal communication with the ircs service
- */
-public class RcsGroupThreadParticipantLeftEventDescriptor extends RcsGroupThreadEventDescriptor {
- private int mLeavingParticipantId;
-
- public RcsGroupThreadParticipantLeftEventDescriptor(long timestamp, int rcsGroupThreadId,
- int originatingParticipantId, int leavingParticipantId) {
- super(timestamp, rcsGroupThreadId, originatingParticipantId);
- mLeavingParticipantId = leavingParticipantId;
- }
-
- @Override
- @VisibleForTesting(visibility = PROTECTED)
- public RcsGroupThreadParticipantLeftEvent createRcsEvent(RcsControllerCall rcsControllerCall) {
- return new RcsGroupThreadParticipantLeftEvent(
- mTimestamp,
- new RcsGroupThread(rcsControllerCall, mRcsGroupThreadId),
- new RcsParticipant(rcsControllerCall, mOriginatingParticipantId),
- new RcsParticipant(rcsControllerCall, mLeavingParticipantId));
- }
-
- @NonNull
- public static final Parcelable.Creator<RcsGroupThreadParticipantLeftEventDescriptor> CREATOR =
- new Creator<RcsGroupThreadParticipantLeftEventDescriptor>() {
- @Override
- public RcsGroupThreadParticipantLeftEventDescriptor createFromParcel(Parcel in) {
- return new RcsGroupThreadParticipantLeftEventDescriptor(in);
- }
-
- @Override
- public RcsGroupThreadParticipantLeftEventDescriptor[] newArray(int size) {
- return new RcsGroupThreadParticipantLeftEventDescriptor[size];
- }
- };
-
- protected RcsGroupThreadParticipantLeftEventDescriptor(Parcel in) {
- super(in);
- mLeavingParticipantId = in.readInt();
- }
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public void writeToParcel(Parcel dest, int flags) {
- super.writeToParcel(dest, flags);
- dest.writeInt(mLeavingParticipantId);
- }
-}
diff --git a/telephony/java/android/telephony/ims/RcsIncomingMessage.java b/telephony/java/android/telephony/ims/RcsIncomingMessage.java
deleted file mode 100644
index 2810a49..0000000
--- a/telephony/java/android/telephony/ims/RcsIncomingMessage.java
+++ /dev/null
@@ -1,103 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.telephony.ims;
-
-import android.annotation.WorkerThread;
-
-/**
- * This is a single instance of a message received over RCS.
- *
- * @hide
- */
-public class RcsIncomingMessage extends RcsMessage {
- /**
- * @hide
- */
- RcsIncomingMessage(RcsControllerCall rcsControllerCall, int id) {
- super(rcsControllerCall, id);
- }
-
- /**
- * Sets the timestamp of arrival for this message and persists into storage. The timestamp is
- * defined as milliseconds passed after midnight, January 1, 1970 UTC
- *
- * @param arrivalTimestamp The timestamp to set to.
- * @throws RcsMessageStoreException if the value could not be persisted into storage
- */
- @WorkerThread
- public void setArrivalTimestamp(long arrivalTimestamp) throws RcsMessageStoreException {
- mRcsControllerCall.callWithNoReturn(
- (iRcs, callingPackage) -> iRcs.setMessageArrivalTimestamp(mId, true,
- arrivalTimestamp, callingPackage));
- }
-
- /**
- * @return Returns the timestamp of arrival for this message. The timestamp is defined as
- * milliseconds passed after midnight, January 1, 1970 UTC
- * @throws RcsMessageStoreException if the value could not be read from the storage
- */
- @WorkerThread
- public long getArrivalTimestamp() throws RcsMessageStoreException {
- return mRcsControllerCall.call(
- (iRcs, callingPackage) -> iRcs.getMessageArrivalTimestamp(mId, true,
- callingPackage));
- }
-
- /**
- * Sets the timestamp of when the user saw this message and persists into storage. The timestamp
- * is defined as milliseconds passed after midnight, January 1, 1970 UTC
- *
- * @param notifiedTimestamp The timestamp to set to.
- * @throws RcsMessageStoreException if the value could not be persisted into storage
- */
- @WorkerThread
- public void setSeenTimestamp(long notifiedTimestamp) throws RcsMessageStoreException {
- mRcsControllerCall.callWithNoReturn(
- (iRcs, callingPackage) -> iRcs.setMessageSeenTimestamp(mId, true, notifiedTimestamp,
- callingPackage));
- }
-
- /**
- * @return Returns the timestamp of when the user saw this message. The timestamp is defined as
- * milliseconds passed after midnight, January 1, 1970 UTC
- * @throws RcsMessageStoreException if the value could not be read from the storage
- */
- @WorkerThread
- public long getSeenTimestamp() throws RcsMessageStoreException {
- return mRcsControllerCall.call(
- (iRcs, callingPackage) -> iRcs.getMessageSeenTimestamp(mId, true, callingPackage));
- }
-
- /**
- * @return Returns the sender of this incoming message.
- * @throws RcsMessageStoreException if the value could not be read from the storage
- */
- @WorkerThread
- public RcsParticipant getSenderParticipant() throws RcsMessageStoreException {
- return new RcsParticipant(
- mRcsControllerCall,
- mRcsControllerCall.call(
- (iRcs, callingPackage) -> iRcs.getSenderParticipant(mId, callingPackage)));
- }
-
- /**
- * @return Returns {@code true} as this is an incoming message
- */
- @Override
- public boolean isIncoming() {
- return true;
- }
-}
diff --git a/telephony/java/android/telephony/ims/RcsIncomingMessageCreationParams.aidl b/telephony/java/android/telephony/ims/RcsIncomingMessageCreationParams.aidl
deleted file mode 100644
index 1f1d4f6..0000000
--- a/telephony/java/android/telephony/ims/RcsIncomingMessageCreationParams.aidl
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- *
- * Copyright 2019, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.telephony.ims;
-
-parcelable RcsIncomingMessageCreationParams;
diff --git a/telephony/java/android/telephony/ims/RcsIncomingMessageCreationParams.java b/telephony/java/android/telephony/ims/RcsIncomingMessageCreationParams.java
deleted file mode 100644
index d95dc4f..0000000
--- a/telephony/java/android/telephony/ims/RcsIncomingMessageCreationParams.java
+++ /dev/null
@@ -1,181 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.telephony.ims;
-
-import android.annotation.CheckResult;
-import android.annotation.NonNull;
-import android.os.Parcel;
-import android.os.Parcelable;
-
-/**
- * {@link RcsIncomingMessageCreationParams} is a collection of parameters that should be passed
- * into {@link RcsThread#addIncomingMessage(RcsIncomingMessageCreationParams)} to generate an
- * {@link RcsIncomingMessage} on that {@link RcsThread}
- *
- * @hide
- */
-public final class RcsIncomingMessageCreationParams extends RcsMessageCreationParams implements
- Parcelable {
- // The arrival timestamp for the RcsIncomingMessage to be created
- private final long mArrivalTimestamp;
- // The seen timestamp for the RcsIncomingMessage to be created
- private final long mSeenTimestamp;
- // The participant that sent this incoming message
- private final int mSenderParticipantId;
-
- /**
- * Builder to help create an {@link RcsIncomingMessageCreationParams}
- *
- * @see RcsThread#addIncomingMessage(RcsIncomingMessageCreationParams)
- */
- public static class Builder extends RcsMessageCreationParams.Builder {
- private RcsParticipant mSenderParticipant;
- private long mArrivalTimestamp;
- private long mSeenTimestamp;
-
- /**
- * Creates a {@link Builder} to create an instance of
- * {@link RcsIncomingMessageCreationParams}
- *
- * @param originationTimestamp The timestamp of {@link RcsMessage} creation. The origination
- * timestamp value in milliseconds passed after midnight,
- * January 1, 1970 UTC
- * @param arrivalTimestamp The timestamp of arrival, defined as milliseconds passed after
- * midnight, January 1, 1970 UTC
- * @param subscriptionId The subscription ID that was used to send or receive this
- * {@link RcsMessage}
- */
- public Builder(long originationTimestamp, long arrivalTimestamp, int subscriptionId) {
- super(originationTimestamp, subscriptionId);
- mArrivalTimestamp = arrivalTimestamp;
- }
-
- /**
- * Sets the {@link RcsParticipant} that send this {@link RcsIncomingMessage}
- *
- * @param senderParticipant The {@link RcsParticipant} that sent this
- * {@link RcsIncomingMessage}
- * @return The same instance of {@link Builder} to chain methods.
- */
- @CheckResult
- public Builder setSenderParticipant(RcsParticipant senderParticipant) {
- mSenderParticipant = senderParticipant;
- return this;
- }
-
- /**
- * Sets the time of the arrival of this {@link RcsIncomingMessage}
-
- * @return The same instance of {@link Builder} to chain methods.
- * @see RcsIncomingMessage#setArrivalTimestamp(long)
- */
- @CheckResult
- public Builder setArrivalTimestamp(long arrivalTimestamp) {
- mArrivalTimestamp = arrivalTimestamp;
- return this;
- }
-
- /**
- * Sets the time of the when this user saw the {@link RcsIncomingMessage}
- * @param seenTimestamp The seen timestamp , defined as milliseconds passed after midnight,
- * January 1, 1970 UTC
- * @return The same instance of {@link Builder} to chain methods.
- * @see RcsIncomingMessage#setSeenTimestamp(long)
- */
- @CheckResult
- public Builder setSeenTimestamp(long seenTimestamp) {
- mSeenTimestamp = seenTimestamp;
- return this;
- }
-
- /**
- * Creates parameters for creating a new incoming message.
- * @return A new instance of {@link RcsIncomingMessageCreationParams} to create a new
- * {@link RcsIncomingMessage}
- */
- public RcsIncomingMessageCreationParams build() {
- return new RcsIncomingMessageCreationParams(this);
- }
- }
-
- private RcsIncomingMessageCreationParams(Builder builder) {
- super(builder);
- mArrivalTimestamp = builder.mArrivalTimestamp;
- mSeenTimestamp = builder.mSeenTimestamp;
- mSenderParticipantId = builder.mSenderParticipant.getId();
- }
-
- private RcsIncomingMessageCreationParams(Parcel in) {
- super(in);
- mArrivalTimestamp = in.readLong();
- mSeenTimestamp = in.readLong();
- mSenderParticipantId = in.readInt();
- }
-
- /**
- * @return Returns the arrival timestamp for the {@link RcsIncomingMessage} to be created.
- * Timestamp is defined as milliseconds passed after midnight, January 1, 1970 UTC
- */
- public long getArrivalTimestamp() {
- return mArrivalTimestamp;
- }
-
- /**
- * @return Returns the seen timestamp for the {@link RcsIncomingMessage} to be created.
- * Timestamp is defined as milliseconds passed after midnight, January 1, 1970 UTC
- */
- public long getSeenTimestamp() {
- return mSeenTimestamp;
- }
-
- /**
- * Helper getter for {@link com.android.internal.telephony.ims.RcsMessageStoreController} to
- * create {@link RcsIncomingMessage}s
- *
- * Since the API doesn't expose any ID's to API users, this should be hidden.
- * @hide
- */
- public int getSenderParticipantId() {
- return mSenderParticipantId;
- }
-
- public static final @NonNull Creator<RcsIncomingMessageCreationParams> CREATOR =
- new Creator<RcsIncomingMessageCreationParams>() {
- @Override
- public RcsIncomingMessageCreationParams createFromParcel(Parcel in) {
- return new RcsIncomingMessageCreationParams(in);
- }
-
- @Override
- public RcsIncomingMessageCreationParams[] newArray(int size) {
- return new RcsIncomingMessageCreationParams[size];
- }
- };
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public void writeToParcel(Parcel dest, int flags) {
- super.writeToParcel(dest);
- dest.writeLong(mArrivalTimestamp);
- dest.writeLong(mSeenTimestamp);
- dest.writeInt(mSenderParticipantId);
- }
-}
diff --git a/telephony/java/android/telephony/ims/RcsMessage.java b/telephony/java/android/telephony/ims/RcsMessage.java
deleted file mode 100644
index 4601bfd..0000000
--- a/telephony/java/android/telephony/ims/RcsMessage.java
+++ /dev/null
@@ -1,358 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.telephony.ims;
-
-import android.annotation.IntDef;
-import android.annotation.NonNull;
-import android.annotation.WorkerThread;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.Set;
-
-/**
- * This is a single instance of a message sent or received over RCS.
- *
- * @hide
- */
-public abstract class RcsMessage {
- /**
- * The value to indicate that this {@link RcsMessage} does not have any location information.
- */
- public static final double LOCATION_NOT_SET = Double.MIN_VALUE;
-
- /**
- * The status to indicate that this {@link RcsMessage}s status is not set yet.
- */
- public static final int NOT_SET = 0;
-
- /**
- * The status to indicate that this {@link RcsMessage} is a draft and is not in the process of
- * sending yet.
- */
- public static final int DRAFT = 1;
-
- /**
- * The status to indicate that this {@link RcsMessage} was successfully sent.
- */
- public static final int QUEUED = 2;
-
- /**
- * The status to indicate that this {@link RcsMessage} is actively being sent.
- */
- public static final int SENDING = 3;
-
- /**
- * The status to indicate that this {@link RcsMessage} was successfully sent.
- */
- public static final int SENT = 4;
-
- /**
- * The status to indicate that this {@link RcsMessage} failed to send in an attempt before, and
- * now being retried.
- */
- public static final int RETRYING = 5;
-
- /**
- * The status to indicate that this {@link RcsMessage} has permanently failed to send.
- */
- public static final int FAILED = 6;
-
- /**
- * The status to indicate that this {@link RcsMessage} was successfully received.
- */
- public static final int RECEIVED = 7;
-
- /**
- * The status to indicate that this {@link RcsMessage} was seen.
- */
- public static final int SEEN = 9;
-
- /**
- * @hide
- */
- protected final RcsControllerCall mRcsControllerCall;
-
- /**
- * @hide
- */
- protected final int mId;
-
- @IntDef({
- DRAFT, QUEUED, SENDING, SENT, RETRYING, FAILED, RECEIVED, SEEN
- })
- @Retention(RetentionPolicy.SOURCE)
- public @interface RcsMessageStatus {
- }
-
- RcsMessage(RcsControllerCall rcsControllerCall, int id) {
- mRcsControllerCall = rcsControllerCall;
- mId = id;
- }
-
- /**
- * Returns the row Id from the common message.
- *
- * @hide
- */
- public int getId() {
- return mId;
- }
-
- /**
- * @return Returns the subscription ID that this {@link RcsMessage} was sent from, or delivered
- * to.
- * @throws RcsMessageStoreException if the value could not be read from the storage
- * @see android.telephony.SubscriptionInfo#getSubscriptionId
- */
- public int getSubscriptionId() throws RcsMessageStoreException {
- return mRcsControllerCall.call(
- (iRcs, callingPackage) -> iRcs.getMessageSubId(mId, isIncoming(), callingPackage));
- }
-
- /**
- * Sets the subscription ID that this {@link RcsMessage} was sent from, or delivered to and
- * persists it into storage.
- *
- * @param subId The subscription ID to persists into storage.
- * @throws RcsMessageStoreException if the value could not be persisted into storage
- * @see android.telephony.SubscriptionInfo#getSubscriptionId
- */
- @WorkerThread
- public void setSubscriptionId(int subId) throws RcsMessageStoreException {
- mRcsControllerCall.callWithNoReturn(
- (iRcs, callingPackage) -> iRcs.setMessageSubId(mId, isIncoming(), subId,
- callingPackage));
- }
-
- /**
- * Sets the status of this message and persists it into storage. Please see
- * {@link RcsFileTransferPart#setFileTransferStatus(int)} to set statuses around file transfers.
- *
- * @throws RcsMessageStoreException if the value could not be persisted into storage
- */
- @WorkerThread
- public void setStatus(@RcsMessageStatus int rcsMessageStatus) throws RcsMessageStoreException {
- mRcsControllerCall.callWithNoReturn(
- (iRcs, callingPackage) -> iRcs.setMessageStatus(mId, isIncoming(), rcsMessageStatus,
- callingPackage));
- }
-
- /**
- * @return Returns the status of this message. Please see
- * {@link RcsFileTransferPart#setFileTransferStatus(int)} to set statuses around file transfers.
- * @throws RcsMessageStoreException if the value could not be read from the storage
- */
- @WorkerThread
- public @RcsMessageStatus int getStatus() throws RcsMessageStoreException {
- return mRcsControllerCall.call(
- (iRcs, callingPackage) -> iRcs.getMessageStatus(mId, isIncoming(), callingPackage));
- }
-
- /**
- * Sets the origination timestamp of this message and persists it into storage. Origination is
- * defined as when the sender tapped the send button.
- *
- * @param timestamp The origination timestamp value in milliseconds passed after midnight,
- * January 1, 1970 UTC
- * @throws RcsMessageStoreException if the value could not be persisted into storage
- */
- @WorkerThread
- public void setOriginationTimestamp(long timestamp) throws RcsMessageStoreException {
- mRcsControllerCall.callWithNoReturn(
- (iRcs, callingPackage) -> iRcs.setMessageOriginationTimestamp(mId, isIncoming(),
- timestamp, callingPackage));
- }
-
- /**
- * @return Returns the origination timestamp of this message in milliseconds passed after
- * midnight, January 1, 1970 UTC. Origination is defined as when the sender tapped the send
- * button.
- * @throws RcsMessageStoreException if the value could not be read from the storage
- */
- @WorkerThread
- public long getOriginationTimestamp() throws RcsMessageStoreException {
- return mRcsControllerCall.call(
- (iRcs, callingPackage) -> iRcs.getMessageOriginationTimestamp(mId, isIncoming(),
- callingPackage));
- }
-
- /**
- * Sets the globally unique RCS message identifier for this message and persists it into
- * storage. This function does not confirm that this message id is unique. Please see 4.4.5.2
- * - GSMA RCC.53 (RCS Device API 1.6 Specification
- *
- * @param rcsMessageGlobalId The globally RCS message identifier
- * @throws RcsMessageStoreException if the value could not be persisted into storage
- */
- @WorkerThread
- public void setRcsMessageId(String rcsMessageGlobalId) throws RcsMessageStoreException {
- mRcsControllerCall.callWithNoReturn(
- (iRcs, callingPackage) -> iRcs.setGlobalMessageIdForMessage(mId, isIncoming(),
- rcsMessageGlobalId, callingPackage));
- }
-
- /**
- * @return Returns the globally unique RCS message identifier for this message. Please see
- * 4.4.5.2 - GSMA RCC.53 (RCS Device API 1.6 Specification
- * @throws RcsMessageStoreException if the value could not be read from the storage
- */
- @WorkerThread
- public String getRcsMessageId() throws RcsMessageStoreException {
- return mRcsControllerCall.call(
- (iRcs, callingPackage) -> iRcs.getGlobalMessageIdForMessage(mId, isIncoming(),
- callingPackage));
- }
-
- /**
- * @return Returns the user visible text included in this message.
- * @throws RcsMessageStoreException if the value could not be read from the storage
- */
- @WorkerThread
- public String getText() throws RcsMessageStoreException {
- return mRcsControllerCall.call(
- (iRcs, callingPackage) -> iRcs.getTextForMessage(mId, isIncoming(),
- callingPackage));
- }
-
- /**
- * Sets the user visible text for this message and persists in storage.
- *
- * @param text The text this message now has
- * @throws RcsMessageStoreException if the value could not be persisted into storage
- */
- @WorkerThread
- public void setText(String text) throws RcsMessageStoreException {
- mRcsControllerCall.callWithNoReturn(
- (iRcs, callingPackage) -> iRcs.setTextForMessage(mId, isIncoming(), text,
- callingPackage));
- }
-
- /**
- * @return Returns the associated latitude for this message, or
- * {@link RcsMessage#LOCATION_NOT_SET} if it does not contain a location.
- * @throws RcsMessageStoreException if the value could not be read from the storage
- */
- @WorkerThread
- public double getLatitude() throws RcsMessageStoreException {
- return mRcsControllerCall.call(
- (iRcs, callingPackage) -> iRcs.getLatitudeForMessage(mId, isIncoming(),
- callingPackage));
- }
-
- /**
- * Sets the latitude for this message and persists in storage.
- *
- * @param latitude The latitude for this location message.
- * @throws RcsMessageStoreException if the value could not be persisted into storage
- */
- @WorkerThread
- public void setLatitude(double latitude) throws RcsMessageStoreException {
- mRcsControllerCall.callWithNoReturn(
- (iRcs, callingPackage) -> iRcs.setLatitudeForMessage(mId, isIncoming(), latitude,
- callingPackage));
- }
-
- /**
- * @return Returns the associated longitude for this message, or
- * {@link RcsMessage#LOCATION_NOT_SET} if it does not contain a location.
- * @throws RcsMessageStoreException if the value could not be read from the storage
- */
- @WorkerThread
- public double getLongitude() throws RcsMessageStoreException {
- return mRcsControllerCall.call(
- (iRcs, callingPackage) -> iRcs.getLongitudeForMessage(mId, isIncoming(),
- callingPackage));
- }
-
- /**
- * Sets the longitude for this message and persists in storage.
- *
- * @param longitude The longitude for this location message.
- * @throws RcsMessageStoreException if the value could not be persisted into storage
- */
- @WorkerThread
- public void setLongitude(double longitude) throws RcsMessageStoreException {
- mRcsControllerCall.callWithNoReturn(
- (iRcs, callingPackage) -> iRcs.setLongitudeForMessage(mId, isIncoming(), longitude,
- callingPackage));
- }
-
- /**
- * Attaches an {@link RcsFileTransferPart} to this message and persists into storage.
- *
- * @param fileTransferCreationParameters The parameters to be used to create the
- * {@link RcsFileTransferPart}
- * @return A new instance of {@link RcsFileTransferPart}
- * @throws RcsMessageStoreException if the file transfer could not be persisted into storage.
- */
- @NonNull
- @WorkerThread
- public RcsFileTransferPart insertFileTransfer(
- RcsFileTransferCreationParams fileTransferCreationParameters)
- throws RcsMessageStoreException {
- return new RcsFileTransferPart(mRcsControllerCall, mRcsControllerCall.call(
- (iRcs, callingPackage) -> iRcs.storeFileTransfer(mId, isIncoming(),
- fileTransferCreationParameters, callingPackage)));
- }
-
- /**
- * @return Returns all the {@link RcsFileTransferPart}s associated with this message in an
- * unmodifiable set.
- * @throws RcsMessageStoreException if the file transfers could not be read from the storage
- */
- @NonNull
- @WorkerThread
- public Set<RcsFileTransferPart> getFileTransferParts() throws RcsMessageStoreException {
- Set<RcsFileTransferPart> fileTransferParts = new HashSet<>();
-
- int[] fileTransferIds = mRcsControllerCall.call(
- (iRcs, callingPackage) -> iRcs.getFileTransfersAttachedToMessage(mId, isIncoming(),
- callingPackage));
-
- for (int fileTransfer : fileTransferIds) {
- fileTransferParts.add(new RcsFileTransferPart(mRcsControllerCall, fileTransfer));
- }
-
- return Collections.unmodifiableSet(fileTransferParts);
- }
-
- /**
- * Removes a {@link RcsFileTransferPart} from this message, and deletes it in storage.
- *
- * @param fileTransferPart The part to delete.
- * @throws RcsMessageStoreException if the file transfer could not be removed from storage
- */
- @WorkerThread
- public void removeFileTransferPart(@NonNull RcsFileTransferPart fileTransferPart)
- throws RcsMessageStoreException {
- if (fileTransferPart == null) {
- return;
- }
-
- mRcsControllerCall.callWithNoReturn(
- (iRcs, callingPackage) -> iRcs.deleteFileTransfer(fileTransferPart.getId(),
- callingPackage));
- }
-
- /**
- * @return Returns {@code true} if this message was received on this device, {@code false} if it
- * was sent.
- */
- public abstract boolean isIncoming();
-}
diff --git a/telephony/java/android/telephony/ims/RcsMessageCreationParams.java b/telephony/java/android/telephony/ims/RcsMessageCreationParams.java
deleted file mode 100644
index f0eea88..0000000
--- a/telephony/java/android/telephony/ims/RcsMessageCreationParams.java
+++ /dev/null
@@ -1,242 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.telephony.ims;
-
-import static android.telephony.ims.RcsMessage.LOCATION_NOT_SET;
-
-import android.annotation.CheckResult;
-import android.annotation.Nullable;
-import android.os.Parcel;
-
-/**
- * The collection of parameters to be passed into
- * {@link RcsThread#addIncomingMessage(RcsIncomingMessageCreationParams)} and
- * {@link RcsThread#addOutgoingMessage(RcsOutgoingMessageCreationParams)} to create and persist
- * {@link RcsMessage}s on an {@link RcsThread}
- *
- * @hide
- */
-public class RcsMessageCreationParams {
- // The globally unique id of the RcsMessage to be created.
- private final String mRcsMessageGlobalId;
-
- // The subscription that this message was/will be received/sent from.
- private final int mSubId;
- // The sending/receiving status of the message
- private final @RcsMessage.RcsMessageStatus int mMessageStatus;
- // The timestamp of message creation
- private final long mOriginationTimestamp;
- // The user visible content of the message
- private final String mText;
- // The latitude of the message if this is a location message
- private final double mLatitude;
- // The longitude of the message if this is a location message
- private final double mLongitude;
-
- /**
- * @return Returns the globally unique RCS Message ID for the {@link RcsMessage} to be created.
- * Please see 4.4.5.2 - GSMA RCC.53 (RCS Device API 1.6 Specification
- */
- @Nullable
- public String getRcsMessageGlobalId() {
- return mRcsMessageGlobalId;
- }
-
- /**
- * @return Returns the subscription ID that was used to send or receive the {@link RcsMessage}
- * to be created.
- */
- public int getSubId() {
- return mSubId;
- }
-
- /**
- * @return Returns the status for the {@link RcsMessage} to be created.
- * @see RcsMessage.RcsMessageStatus
- */
- public int getMessageStatus() {
- return mMessageStatus;
- }
-
- /**
- * @return Returns the origination timestamp of the {@link RcsMessage} to be created in
- * milliseconds passed after midnight, January 1, 1970 UTC. Origination is defined as when
- * the sender tapped the send button.
- */
- public long getOriginationTimestamp() {
- return mOriginationTimestamp;
- }
-
- /**
- * @return Returns the user visible text contained in the {@link RcsMessage} to be created
- */
- @Nullable
- public String getText() {
- return mText;
- }
-
- /**
- * @return Returns the latitude of the {@link RcsMessage} to be created, or
- * {@link RcsMessage#LOCATION_NOT_SET} if the message does not contain a location.
- */
- public double getLatitude() {
- return mLatitude;
- }
-
- /**
- * @return Returns the longitude of the {@link RcsMessage} to be created, or
- * {@link RcsMessage#LOCATION_NOT_SET} if the message does not contain a location.
- */
- public double getLongitude() {
- return mLongitude;
- }
-
- /**
- * The base builder for creating {@link RcsMessage}s on {@link RcsThread}s.
- *
- * @see RcsIncomingMessageCreationParams
- */
- public static class Builder {
- private String mRcsMessageGlobalId;
- private int mSubId;
- private @RcsMessage.RcsMessageStatus int mMessageStatus;
- private long mOriginationTimestamp;
- private String mText;
- private double mLatitude = LOCATION_NOT_SET;
- private double mLongitude = LOCATION_NOT_SET;
-
- /**
- * @hide
- */
- public Builder(long originationTimestamp, int subscriptionId) {
- mOriginationTimestamp = originationTimestamp;
- mSubId = subscriptionId;
- }
-
- /**
- * Sets the status of the {@link RcsMessage} to be built.
- *
- * @param rcsMessageStatus The status to be set
- * @return The same instance of {@link Builder} to chain methods
- * @see RcsMessage#setStatus(int)
- */
- @CheckResult
- public Builder setStatus(@RcsMessage.RcsMessageStatus int rcsMessageStatus) {
- mMessageStatus = rcsMessageStatus;
- return this;
- }
-
- /**
- * Sets the globally unique RCS message identifier for the {@link RcsMessage} to be built.
- * This function does not confirm that this message id is unique. Please see 4.4.5.2 - GSMA
- * RCC.53 (RCS Device API 1.6 Specification)
- *
- * @param rcsMessageId The ID to be set
- * @return The same instance of {@link Builder} to chain methods
- * @see RcsMessage#setRcsMessageId(String)
- */
- @CheckResult
- public Builder setRcsMessageId(String rcsMessageId) {
- mRcsMessageGlobalId = rcsMessageId;
- return this;
- }
-
- /**
- * Sets the text of the {@link RcsMessage} to be built.
- *
- * @param text The user visible text of the message
- * @return The same instance of {@link Builder} to chain methods
- * @see RcsMessage#setText(String)
- */
- @CheckResult
- public Builder setText(String text) {
- mText = text;
- return this;
- }
-
- /**
- * Sets the latitude of the {@link RcsMessage} to be built. Please see US5-24 - GSMA RCC.71
- * (RCS Universal Profile Service Definition Document)
- *
- * @param latitude The latitude of the location information associated with this message.
- * @return The same instance of {@link Builder} to chain methods
- * @see RcsMessage#setLatitude(double)
- */
- @CheckResult
- public Builder setLatitude(double latitude) {
- mLatitude = latitude;
- return this;
- }
-
- /**
- * Sets the longitude of the {@link RcsMessage} to be built. Please see US5-24 - GSMA RCC.71
- * (RCS Universal Profile Service Definition Document)
- *
- * @param longitude The longitude of the location information associated with this message.
- * @return The same instance of {@link Builder} to chain methods
- * @see RcsMessage#setLongitude(double)
- */
- @CheckResult
- public Builder setLongitude(double longitude) {
- mLongitude = longitude;
- return this;
- }
-
- /**
- * @return Builds and returns a newly created {@link RcsMessageCreationParams}
- */
- public RcsMessageCreationParams build() {
- return new RcsMessageCreationParams(this);
- }
- }
-
- protected RcsMessageCreationParams(Builder builder) {
- mRcsMessageGlobalId = builder.mRcsMessageGlobalId;
- mSubId = builder.mSubId;
- mMessageStatus = builder.mMessageStatus;
- mOriginationTimestamp = builder.mOriginationTimestamp;
- mText = builder.mText;
- mLatitude = builder.mLatitude;
- mLongitude = builder.mLongitude;
- }
-
- /**
- * @hide
- */
- RcsMessageCreationParams(Parcel in) {
- mRcsMessageGlobalId = in.readString();
- mSubId = in.readInt();
- mMessageStatus = in.readInt();
- mOriginationTimestamp = in.readLong();
- mText = in.readString();
- mLatitude = in.readDouble();
- mLongitude = in.readDouble();
- }
-
- /**
- * @hide
- */
- public void writeToParcel(Parcel dest) {
- dest.writeString(mRcsMessageGlobalId);
- dest.writeInt(mSubId);
- dest.writeInt(mMessageStatus);
- dest.writeLong(mOriginationTimestamp);
- dest.writeString(mText);
- dest.writeDouble(mLatitude);
- dest.writeDouble(mLongitude);
- }
-}
diff --git a/telephony/java/android/telephony/ims/RcsMessageManager.java b/telephony/java/android/telephony/ims/RcsMessageManager.java
deleted file mode 100644
index a1c7c0f..0000000
--- a/telephony/java/android/telephony/ims/RcsMessageManager.java
+++ /dev/null
@@ -1,281 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.telephony.ims;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.annotation.SystemService;
-import android.annotation.WorkerThread;
-import android.content.Context;
-import android.net.Uri;
-
-import java.util.List;
-
-/**
- * RcsMessageManager is the application interface to RcsProvider and provides access methods to
- * RCS related database tables.
- *
- * @hide
- */
-@SystemService(Context.TELEPHONY_RCS_MESSAGE_SERVICE)
-public class RcsMessageManager {
- RcsControllerCall mRcsControllerCall;
-
- /**
- * Use {@link Context#getSystemService(String)} to get an instance of this service.
- * @hide
- */
- public RcsMessageManager(Context context) {
- mRcsControllerCall = new RcsControllerCall(context);
- }
-
- /**
- * Returns the first chunk of existing {@link RcsThread}s in the common storage.
- *
- * @param queryParameters Parameters to specify to return a subset of all RcsThreads.
- * Passing a value of null will return all threads.
- * @throws RcsMessageStoreException if the query could not be completed on the storage
- */
- @WorkerThread
- @NonNull
- public RcsThreadQueryResult getRcsThreads(@Nullable RcsThreadQueryParams queryParameters)
- throws RcsMessageStoreException {
- return new RcsThreadQueryResult(mRcsControllerCall,
- mRcsControllerCall.call(
- (iRcs, callingPackage) -> iRcs.getRcsThreads(queryParameters,
- callingPackage)));
- }
-
- /**
- * Returns the next chunk of {@link RcsThread}s in the common storage.
- *
- * @param continuationToken A token to continue the query to get the next chunk. This is
- * obtained through {@link RcsThreadQueryResult#getContinuationToken}.
- * @throws RcsMessageStoreException if the query could not be completed on the storage
- */
- @WorkerThread
- @NonNull
- public RcsThreadQueryResult getRcsThreads(@NonNull RcsQueryContinuationToken continuationToken)
- throws RcsMessageStoreException {
- return new RcsThreadQueryResult(mRcsControllerCall,
- mRcsControllerCall.call(
- (iRcs, callingPackage) -> iRcs.getRcsThreadsWithToken(continuationToken,
- callingPackage)));
- }
-
- /**
- * Returns the first chunk of existing {@link RcsParticipant}s in the common storage.
- *
- * @param queryParameters Parameters to specify to return a subset of all RcsParticipants.
- * Passing a value of null will return all participants.
- * @throws RcsMessageStoreException if the query could not be completed on the storage
- */
- @WorkerThread
- @NonNull
- public RcsParticipantQueryResult getRcsParticipants(
- @Nullable RcsParticipantQueryParams queryParameters)
- throws RcsMessageStoreException {
- return new RcsParticipantQueryResult(mRcsControllerCall,
- mRcsControllerCall.call(
- (iRcs, callingPackage) -> iRcs.getParticipants(queryParameters,
- callingPackage)));
- }
-
- /**
- * Returns the next chunk of {@link RcsParticipant}s in the common storage.
- *
- * @param continuationToken A token to continue the query to get the next chunk. This is
- * obtained through
- * {@link RcsParticipantQueryResult#getContinuationToken}
- * @throws RcsMessageStoreException if the query could not be completed on the storage
- */
- @WorkerThread
- @NonNull
- public RcsParticipantQueryResult getRcsParticipants(
- @NonNull RcsQueryContinuationToken continuationToken)
- throws RcsMessageStoreException {
- return new RcsParticipantQueryResult(mRcsControllerCall,
- mRcsControllerCall.call(
- (iRcs, callingPackage) -> iRcs.getParticipantsWithToken(continuationToken,
- callingPackage)));
- }
-
- /**
- * Returns the first chunk of existing {@link RcsMessage}s in the common storage.
- *
- * @param queryParams Parameters to specify to return a subset of all RcsMessages.
- * Passing a value of null will return all messages.
- * @throws RcsMessageStoreException if the query could not be completed on the storage
- */
- @WorkerThread
- @NonNull
- public RcsMessageQueryResult getRcsMessages(
- @Nullable RcsMessageQueryParams queryParams) throws RcsMessageStoreException {
- return new RcsMessageQueryResult(mRcsControllerCall,
- mRcsControllerCall.call(
- (iRcs, callingPackage) -> iRcs.getMessages(queryParams, callingPackage)));
- }
-
- /**
- * Returns the next chunk of {@link RcsMessage}s in the common storage.
- *
- * @param continuationToken A token to continue the query to get the next chunk. This is
- * obtained through {@link RcsMessageQueryResult#getContinuationToken}
- * @throws RcsMessageStoreException if the query could not be completed on the storage
- */
- @WorkerThread
- @NonNull
- public RcsMessageQueryResult getRcsMessages(
- @NonNull RcsQueryContinuationToken continuationToken) throws RcsMessageStoreException {
- return new RcsMessageQueryResult(mRcsControllerCall,
- mRcsControllerCall.call(
- (iRcs, callingPackage) -> iRcs.getMessagesWithToken(continuationToken,
- callingPackage)));
- }
-
- /**
- * Returns the first chunk of existing {@link RcsEvent}s in the common storage.
- *
- * @param queryParams Parameters to specify to return a subset of all RcsEvents.
- * Passing a value of null will return all events.
- * @throws RcsMessageStoreException if the query could not be completed on the storage
- */
- @WorkerThread
- @NonNull
- public RcsEventQueryResult getRcsEvents(
- @Nullable RcsEventQueryParams queryParams) throws RcsMessageStoreException {
- return mRcsControllerCall.call(
- (iRcs, callingPackage) -> iRcs.getEvents(queryParams, callingPackage))
- .getRcsEventQueryResult(mRcsControllerCall);
- }
-
- /**
- * Returns the next chunk of {@link RcsEvent}s in the common storage.
- *
- * @param continuationToken A token to continue the query to get the next chunk. This is
- * obtained through {@link RcsEventQueryResult#getContinuationToken}.
- * @throws RcsMessageStoreException if the query could not be completed on the storage
- */
- @WorkerThread
- @NonNull
- public RcsEventQueryResult getRcsEvents(
- @NonNull RcsQueryContinuationToken continuationToken) throws RcsMessageStoreException {
- return mRcsControllerCall.call(
- (iRcs, callingPackage) -> iRcs.getEventsWithToken(continuationToken,
- callingPackage))
- .getRcsEventQueryResult(mRcsControllerCall);
- }
-
- /**
- * Persists an {@link RcsEvent} to common storage.
- *
- * @param rcsEvent The {@link RcsEvent} to persist into storage.
- * @throws RcsMessageStoreException if the query could not be completed on the storage
- * @see RcsGroupThreadNameChangedEvent
- * @see RcsGroupThreadIconChangedEvent
- * @see RcsGroupThreadParticipantJoinedEvent
- * @see RcsGroupThreadParticipantLeftEvent
- * @see RcsParticipantAliasChangedEvent
- */
- @WorkerThread
- @NonNull
- public void persistRcsEvent(RcsEvent rcsEvent) throws RcsMessageStoreException {
- rcsEvent.persist(mRcsControllerCall);
- }
-
- /**
- * Creates a new 1 to 1 thread with the given participant and persists it in the storage.
- *
- * @param recipient The {@link RcsParticipant} that will receive the messages in this thread.
- * @return The newly created {@link Rcs1To1Thread}
- * @throws RcsMessageStoreException if the thread could not be persisted in the storage
- */
- @WorkerThread
- @NonNull
- public Rcs1To1Thread createRcs1To1Thread(@NonNull RcsParticipant recipient)
- throws RcsMessageStoreException {
- return new Rcs1To1Thread(
- mRcsControllerCall,
- mRcsControllerCall.call(
- (iRcs, callingPackage) -> iRcs.createRcs1To1Thread(recipient.getId(),
- callingPackage)));
- }
-
- /**
- * Creates a new group thread with the given participants and persists it in the storage.
- *
- * @throws RcsMessageStoreException if the thread could not be persisted in the storage
- */
- @WorkerThread
- @NonNull
- public RcsGroupThread createGroupThread(@Nullable List<RcsParticipant> recipients,
- @Nullable String groupName, @Nullable Uri groupIcon) throws RcsMessageStoreException {
- int[] recipientIds = null;
- if (recipients != null) {
- recipientIds = new int[recipients.size()];
-
- for (int i = 0; i < recipients.size(); i++) {
- recipientIds[i] = recipients.get(i).getId();
- }
- }
-
- int[] finalRecipientIds = recipientIds;
-
- int threadId = mRcsControllerCall.call(
- (iRcs, callingPackage) -> iRcs.createGroupThread(finalRecipientIds, groupName,
- groupIcon, callingPackage));
-
- return new RcsGroupThread(mRcsControllerCall, threadId);
- }
-
- /**
- * Delete the given {@link RcsThread} from the storage.
- *
- * @param thread The thread to be deleted.
- * @throws RcsMessageStoreException if the thread could not be deleted from the storage
- */
- @WorkerThread
- public void deleteThread(@NonNull RcsThread thread) throws RcsMessageStoreException {
- if (thread == null) {
- return;
- }
-
- boolean isDeleteSucceeded = mRcsControllerCall.call(
- (iRcs, callingPackage) -> iRcs.deleteThread(thread.getThreadId(),
- thread.getThreadType(), callingPackage));
-
- if (!isDeleteSucceeded) {
- throw new RcsMessageStoreException("Could not delete RcsThread");
- }
- }
-
- /**
- * Creates a new participant and persists it in the storage.
- *
- * @param canonicalAddress The defining address (e.g. phone number) of the participant.
- * @param alias The RCS alias for the participant.
- * @throws RcsMessageStoreException if the participant could not be created on the storage
- */
- @WorkerThread
- @NonNull
- public RcsParticipant createRcsParticipant(String canonicalAddress, @Nullable String alias)
- throws RcsMessageStoreException {
- return new RcsParticipant(mRcsControllerCall, mRcsControllerCall.call(
- (iRcs, callingPackage) -> iRcs.createRcsParticipant(canonicalAddress, alias,
- callingPackage)));
- }
-}
diff --git a/telephony/java/android/telephony/ims/RcsMessageQueryParams.java b/telephony/java/android/telephony/ims/RcsMessageQueryParams.java
deleted file mode 100644
index 9f9eafb..0000000
--- a/telephony/java/android/telephony/ims/RcsMessageQueryParams.java
+++ /dev/null
@@ -1,361 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.telephony.ims;
-
-import android.annotation.CheckResult;
-import android.annotation.IntDef;
-import android.annotation.IntRange;
-import android.annotation.Nullable;
-import android.os.Parcel;
-import android.os.Parcelable;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.security.InvalidParameterException;
-
-/**
- * The parameters to pass into
- * {@link RcsMessageStore#getRcsMessages(RcsMessageQueryParams)} in order to select a
- * subset of {@link RcsMessage}s present in the message store.
- *
- * @hide
- */
-public final class RcsMessageQueryParams implements Parcelable {
- /**
- * @hide - not meant for public use
- */
- public static final int THREAD_ID_NOT_SET = -1;
-
- /**
- * Flag to be used with {@link Builder#setSortProperty(int)} to denote that the results should
- * be sorted in the same order of {@link RcsMessage}s that got persisted into storage for faster
- * results.
- */
- public static final int SORT_BY_CREATION_ORDER = 0;
-
- /**
- * Flag to be used with {@link Builder#setSortProperty(int)} to denote that the results should
- * be sorted according to the timestamp of {@link RcsMessage#getOriginationTimestamp()}
- */
- public static final int SORT_BY_TIMESTAMP = 1;
-
- @Retention(RetentionPolicy.SOURCE)
- @IntDef({SORT_BY_CREATION_ORDER, SORT_BY_TIMESTAMP})
- public @interface SortingProperty {
- }
-
- /**
- * Bitmask flag to be used with {@link Builder#setMessageType(int)} to make
- * {@link RcsMessageStore#getRcsMessages(RcsMessageQueryParams)} return
- * {@link RcsIncomingMessage}s.
- */
- public static final int MESSAGE_TYPE_INCOMING = 0x0001;
-
- /**
- * Bitmask flag to be used with {@link Builder#setMessageType(int)} to make
- * {@link RcsMessageStore#getRcsMessages(RcsMessageQueryParams)} return
- * {@link RcsOutgoingMessage}s.
- */
- public static final int MESSAGE_TYPE_OUTGOING = 0x0002;
-
- /**
- * Bitmask flag to be used with {@link Builder#setFileTransferPresence(int)} to make
- * {@link RcsMessageStore#getRcsMessages(RcsMessageQueryParams)} return {@link RcsMessage}s
- * that have an {@link RcsFileTransferPart} attached.
- */
- public static final int MESSAGES_WITH_FILE_TRANSFERS = 0x0004;
-
- /**
- * Bitmask flag to be used with {@link Builder#setFileTransferPresence(int)} to make
- * {@link RcsMessageStore#getRcsMessages(RcsMessageQueryParams)} return {@link RcsMessage}s
- * that don't have an {@link RcsFileTransferPart} attached.
- */
- public static final int MESSAGES_WITHOUT_FILE_TRANSFERS = 0x0008;
-
- /**
- * @hide - not meant for public use
- */
- public static final String MESSAGE_QUERY_PARAMETERS_KEY = "message_query_parameters";
-
- // Whether the result should be filtered against incoming or outgoing messages
- private int mMessageType;
- // Whether the result should have file transfer messages attached or not
- private int mFileTransferPresence;
- // The SQL "Like" clause to filter messages
- private String mMessageLike;
- // The property the messages should be sorted against
- private @SortingProperty int mSortingProperty;
- // Whether the messages should be sorted in ascending order
- private boolean mIsAscending;
- // The number of results that should be returned with this query
- private int mLimit;
- // The thread that the results should be limited to
- private int mThreadId;
-
- RcsMessageQueryParams(int messageType, int fileTransferPresence, String messageLike,
- int threadId, @SortingProperty int sortingProperty, boolean isAscending, int limit) {
- mMessageType = messageType;
- mFileTransferPresence = fileTransferPresence;
- mMessageLike = messageLike;
- mSortingProperty = sortingProperty;
- mIsAscending = isAscending;
- mLimit = limit;
- mThreadId = threadId;
- }
-
- /**
- * @return Returns the type of {@link RcsMessage}s that this {@link RcsMessageQueryParams}
- * is set to query for.
- */
- public int getMessageType() {
- return mMessageType;
- }
-
- /**
- * @return Returns whether the result query should return {@link RcsMessage}s with
- * {@link RcsFileTransferPart}s or not
- */
- public int getFileTransferPresence() {
- return mFileTransferPresence;
- }
-
- /**
- * @return Returns the SQL-inspired "LIKE" clause that will be used to match {@link RcsMessage}s
- */
- public String getMessageLike() {
- return mMessageLike;
- }
-
- /**
- * @return Returns the number of {@link RcsThread}s to be returned from the query. A value of
- * 0 means there is no set limit.
- */
- public int getLimit() {
- return mLimit;
- }
-
- /**
- * @return Returns the property that will be used to sort the result against.
- * @see SortingProperty
- */
- public @SortingProperty int getSortingProperty() {
- return mSortingProperty;
- }
-
- /**
- * @return Returns {@code true} if the result set will be sorted in ascending order,
- * {@code false} if it will be sorted in descending order.
- */
- public boolean getSortDirection() {
- return mIsAscending;
- }
-
- /**
- * This is used in {@link com.android.internal.telephony.ims.RcsMessageStoreController} to get
- * the thread that the result query should be limited to.
- *
- * As we do not expose any sort of integer ID's to public usage, this should be hidden.
- *
- * @hide - not meant for public use
- */
- public int getThreadId() {
- return mThreadId;
- }
-
- /**
- * A helper class to build the {@link RcsMessageQueryParams}.
- */
- public static class Builder {
- private @SortingProperty int mSortingProperty;
- private int mMessageType;
- private int mFileTransferPresence;
- private String mMessageLike;
- private boolean mIsAscending;
- private int mLimit = 100;
- private int mThreadId = THREAD_ID_NOT_SET;
-
- /**
- * Creates a new builder for {@link RcsMessageQueryParams} to be used in
- * {@link RcsMessageStore#getRcsMessages(RcsMessageQueryParams)}
- *
- */
- public Builder() {
- // empty implementation
- }
-
- /**
- * Desired number of threads to be returned from the query. Passing in 0 will return all
- * existing threads at once. The limit defaults to 100.
- *
- * @param limit The number to limit the query result to.
- * @return The same instance of the builder to chain parameters.
- * @throws InvalidParameterException If the given limit is negative.
- */
- @CheckResult
- public Builder setResultLimit(@IntRange(from = 0) int limit)
- throws InvalidParameterException {
- if (limit < 0) {
- throw new InvalidParameterException("The query limit must be non-negative");
- }
-
- mLimit = limit;
- return this;
- }
-
- /**
- * Sets the type of messages to be returned from the query.
- *
- * @param messageType The type of message to be returned.
- * @return The same instance of the builder to chain parameters.
- * @see RcsMessageQueryParams#MESSAGE_TYPE_INCOMING
- * @see RcsMessageQueryParams#MESSAGE_TYPE_OUTGOING
- */
- @CheckResult
- public Builder setMessageType(int messageType) {
- mMessageType = messageType;
- return this;
- }
-
- /**
- * Sets whether file transfer messages should be included in the query result or not.
- *
- * @param fileTransferPresence Whether file transfers should be included in the result
- * @return The same instance of the builder to chain parameters.
- * @see RcsMessageQueryParams#MESSAGES_WITH_FILE_TRANSFERS
- * @see RcsMessageQueryParams#MESSAGES_WITHOUT_FILE_TRANSFERS
- */
- @CheckResult
- public Builder setFileTransferPresence(int fileTransferPresence) {
- mFileTransferPresence = fileTransferPresence;
- return this;
- }
-
- /**
- * Sets an SQL-inspired "like" clause to match with messages. Using a percent sign ('%')
- * wildcard matches any sequence of zero or more characters. Using an underscore ('_')
- * wildcard matches any single character. Not using any wildcards would only perform a
- * string match. The input string is case-insensitive.
- *
- * The input "Wh%" would match messages "who", "where" and "what", while the input "Wh_"
- * would only match "who"
- *
- * @param messageLike The "like" clause for matching {@link RcsMessage}s.
- * @return The same instance of the builder to chain parameters.
- */
- @CheckResult
- public Builder setMessageLike(String messageLike) {
- mMessageLike = messageLike;
- return this;
- }
-
- /**
- * Sets the property where the results should be sorted against. Defaults to
- * {@link RcsMessageQueryParams.SortingProperty#SORT_BY_CREATION_ORDER}
- *
- * @param sortingProperty against which property the results should be sorted
- * @return The same instance of the builder to chain parameters.
- */
- @CheckResult
- public Builder setSortProperty(@SortingProperty int sortingProperty) {
- mSortingProperty = sortingProperty;
- return this;
- }
-
- /**
- * Sets whether the results should be sorted ascending or descending
- *
- * @param isAscending whether the results should be sorted ascending
- * @return The same instance of the builder to chain parameters.
- */
- @CheckResult
- public Builder setSortDirection(boolean isAscending) {
- mIsAscending = isAscending;
- return this;
- }
-
- /**
- * Limits the results to the given thread.
- *
- * @param thread the {@link RcsThread} that results should be limited to. If set to
- * {@code null}, messages on all threads will be queried
- * @return The same instance of the builder to chain parameters.
- */
- @CheckResult
- public Builder setThread(@Nullable RcsThread thread) {
- if (thread == null) {
- mThreadId = THREAD_ID_NOT_SET;
- } else {
- mThreadId = thread.getThreadId();
- }
- return this;
- }
-
- /**
- * Builds the {@link RcsMessageQueryParams} to use in
- * {@link RcsMessageStore#getRcsMessages(RcsMessageQueryParams)}
- *
- * @return An instance of {@link RcsMessageQueryParams} to use with the message
- * query.
- */
- public RcsMessageQueryParams build() {
- return new RcsMessageQueryParams(mMessageType, mFileTransferPresence, mMessageLike,
- mThreadId, mSortingProperty, mIsAscending, mLimit);
- }
- }
-
- /**
- * Parcelable boilerplate below.
- */
- private RcsMessageQueryParams(Parcel in) {
- mMessageType = in.readInt();
- mFileTransferPresence = in.readInt();
- mMessageLike = in.readString();
- mSortingProperty = in.readInt();
- mIsAscending = in.readBoolean();
- mLimit = in.readInt();
- mThreadId = in.readInt();
- }
-
- public static final @android.annotation.NonNull Creator<RcsMessageQueryParams> CREATOR =
- new Creator<RcsMessageQueryParams>() {
- @Override
- public RcsMessageQueryParams createFromParcel(Parcel in) {
- return new RcsMessageQueryParams(in);
- }
-
- @Override
- public RcsMessageQueryParams[] newArray(int size) {
- return new RcsMessageQueryParams[size];
- }
- };
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public void writeToParcel(Parcel dest, int flags) {
- dest.writeInt(mMessageType);
- dest.writeInt(mFileTransferPresence);
- dest.writeString(mMessageLike);
- dest.writeInt(mSortingProperty);
- dest.writeBoolean(mIsAscending);
- dest.writeInt(mLimit);
- dest.writeInt(mThreadId);
- }
-}
diff --git a/telephony/java/android/telephony/ims/RcsMessageQueryResult.java b/telephony/java/android/telephony/ims/RcsMessageQueryResult.java
deleted file mode 100644
index 36bb78a..0000000
--- a/telephony/java/android/telephony/ims/RcsMessageQueryResult.java
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.telephony.ims;
-
-import static android.provider.Telephony.RcsColumns.RcsUnifiedMessageColumns.MESSAGE_TYPE_INCOMING;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-
-import java.util.List;
-import java.util.stream.Collectors;
-
-/**
- * The result of a {@link RcsMessageStore#getRcsMessages(RcsMessageQueryParams)}
- * call. This class allows getting the token for querying the next batch of messages in order to
- * prevent handling large amounts of data at once.
- *
- * @hide
- */
-public final class RcsMessageQueryResult {
- private final RcsControllerCall mRcsControllerCall;
- private final RcsMessageQueryResultParcelable mRcsMessageQueryResultParcelable;
-
- RcsMessageQueryResult(RcsControllerCall rcsControllerCall,
- RcsMessageQueryResultParcelable rcsMessageQueryResultParcelable) {
- mRcsControllerCall = rcsControllerCall;
- mRcsMessageQueryResultParcelable = rcsMessageQueryResultParcelable;
- }
-
- /**
- * Returns a token to call
- * {@link RcsMessageStore#getRcsMessages(RcsQueryContinuationToken)}
- * to get the next batch of {@link RcsMessage}s.
- */
- @Nullable
- public RcsQueryContinuationToken getContinuationToken() {
- return mRcsMessageQueryResultParcelable.mContinuationToken;
- }
-
- /**
- * Returns all the {@link RcsMessage}s in the current query result. Call {@link
- * RcsMessageStore#getRcsMessages(RcsQueryContinuationToken)} to get the next batch
- * of {@link RcsMessage}s.
- */
- @NonNull
- public List<RcsMessage> getMessages() {
- return mRcsMessageQueryResultParcelable.mMessageTypeIdPairs.stream()
- .map(typeIdPair -> typeIdPair.getType() == MESSAGE_TYPE_INCOMING
- ? new RcsIncomingMessage(mRcsControllerCall, typeIdPair.getId())
- : new RcsOutgoingMessage(mRcsControllerCall, typeIdPair.getId()))
- .collect(Collectors.toList());
- }
-}
diff --git a/telephony/java/android/telephony/ims/RcsMessageQueryResultParcelable.aidl b/telephony/java/android/telephony/ims/RcsMessageQueryResultParcelable.aidl
deleted file mode 100644
index 86928bf..0000000
--- a/telephony/java/android/telephony/ims/RcsMessageQueryResultParcelable.aidl
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- *
- * Copyright 2019, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.telephony.ims;
-
-parcelable RcsMessageQueryResultParcelable;
diff --git a/telephony/java/android/telephony/ims/RcsMessageQueryResultParcelable.java b/telephony/java/android/telephony/ims/RcsMessageQueryResultParcelable.java
deleted file mode 100644
index 4972f9b..0000000
--- a/telephony/java/android/telephony/ims/RcsMessageQueryResultParcelable.java
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.telephony.ims;
-
-import android.os.Parcel;
-import android.os.Parcelable;
-
-import com.android.ims.RcsTypeIdPair;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * @hide - used only for internal communication with the ircs service
- */
-public class RcsMessageQueryResultParcelable implements Parcelable {
- // The token to continue the query to get the next batch of results
- final RcsQueryContinuationToken mContinuationToken;
- // The message type and message ID pairs for all the messages in this query result
- final List<RcsTypeIdPair> mMessageTypeIdPairs;
-
- public RcsMessageQueryResultParcelable(
- RcsQueryContinuationToken continuationToken,
- List<RcsTypeIdPair> messageTypeIdPairs) {
- mContinuationToken = continuationToken;
- mMessageTypeIdPairs = messageTypeIdPairs;
- }
-
- private RcsMessageQueryResultParcelable(Parcel in) {
- mContinuationToken = in.readParcelable(
- RcsQueryContinuationToken.class.getClassLoader());
-
- mMessageTypeIdPairs = new ArrayList<>();
- in.readTypedList(mMessageTypeIdPairs, RcsTypeIdPair.CREATOR);
- }
-
- public static final Creator<RcsMessageQueryResultParcelable> CREATOR =
- new Creator<RcsMessageQueryResultParcelable>() {
- @Override
- public RcsMessageQueryResultParcelable createFromParcel(Parcel in) {
- return new RcsMessageQueryResultParcelable(in);
- }
-
- @Override
- public RcsMessageQueryResultParcelable[] newArray(int size) {
- return new RcsMessageQueryResultParcelable[size];
- }
- };
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public void writeToParcel(Parcel dest, int flags) {
- dest.writeParcelable(mContinuationToken, flags);
- dest.writeTypedList(mMessageTypeIdPairs);
- }
-}
diff --git a/telephony/java/android/telephony/ims/RcsMessageSnippet.aidl b/telephony/java/android/telephony/ims/RcsMessageSnippet.aidl
deleted file mode 100644
index 99b8eb7..0000000
--- a/telephony/java/android/telephony/ims/RcsMessageSnippet.aidl
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- *
- * Copyright 2019, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.telephony.ims;
-
-parcelable RcsMessageSnippet;
diff --git a/telephony/java/android/telephony/ims/RcsMessageSnippet.java b/telephony/java/android/telephony/ims/RcsMessageSnippet.java
deleted file mode 100644
index 8103160..0000000
--- a/telephony/java/android/telephony/ims/RcsMessageSnippet.java
+++ /dev/null
@@ -1,98 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.telephony.ims;
-
-import android.annotation.Nullable;
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.telephony.ims.RcsMessage.RcsMessageStatus;
-
-/**
- * An immutable summary of the latest {@link RcsMessage} on an {@link RcsThread}
- *
- * @hide
- */
-public final class RcsMessageSnippet implements Parcelable {
- private final String mText;
- private final @RcsMessageStatus int mStatus;
- private final long mTimestamp;
-
- /**
- * @hide
- */
- public RcsMessageSnippet(String text, @RcsMessageStatus int status, long timestamp) {
- mText = text;
- mStatus = status;
- mTimestamp = timestamp;
- }
-
- /**
- * @return Returns the text of the {@link RcsMessage} with highest origination timestamp value
- * (i.e. latest) in this thread
- */
- @Nullable
- public String getSnippetText() {
- return mText;
- }
-
- /**
- * @return Returns the status of the {@link RcsMessage} with highest origination timestamp value
- * (i.e. latest) in this thread
- */
- public @RcsMessageStatus int getSnippetStatus() {
- return mStatus;
- }
-
- /**
- * @return Returns the timestamp of the {@link RcsMessage} with highest origination timestamp
- * value (i.e. latest) in this thread
- */
- public long getSnippetTimestamp() {
- return mTimestamp;
- }
-
- private RcsMessageSnippet(Parcel in) {
- mText = in.readString();
- mStatus = in.readInt();
- mTimestamp = in.readLong();
- }
-
- public static final @android.annotation.NonNull Creator<RcsMessageSnippet> CREATOR =
- new Creator<RcsMessageSnippet>() {
- @Override
- public RcsMessageSnippet createFromParcel(Parcel in) {
- return new RcsMessageSnippet(in);
- }
-
- @Override
- public RcsMessageSnippet[] newArray(int size) {
- return new RcsMessageSnippet[size];
- }
- };
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public void writeToParcel(Parcel dest, int flags) {
- dest.writeString(mText);
- dest.writeInt(mStatus);
- dest.writeLong(mTimestamp);
- }
-}
diff --git a/telephony/java/android/telephony/ims/RcsMessageStoreException.java b/telephony/java/android/telephony/ims/RcsMessageStoreException.java
deleted file mode 100644
index 3b3fcf2..0000000
--- a/telephony/java/android/telephony/ims/RcsMessageStoreException.java
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * Copyright (c) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.telephony.ims;
-
-/**
- * An exception that happened on {@link RcsMessageStore} or one of the derived storage classes in
- * {@link android.telephony.ims}
- *
- * @hide
- */
-public class RcsMessageStoreException extends Exception {
-
- /**
- * Constructs an {@link RcsMessageStoreException} with the specified detail message.
- * @param message The detail message
- * @see Throwable#getMessage()
- */
- public RcsMessageStoreException(String message) {
- super(message);
- }
-}
diff --git a/telephony/java/android/telephony/ims/RcsOutgoingMessage.java b/telephony/java/android/telephony/ims/RcsOutgoingMessage.java
deleted file mode 100644
index 7080ec6..0000000
--- a/telephony/java/android/telephony/ims/RcsOutgoingMessage.java
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.telephony.ims;
-
-import android.annotation.NonNull;
-import android.annotation.WorkerThread;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * This is a single instance of a message sent over RCS.
- *
- * @hide
- */
-public class RcsOutgoingMessage extends RcsMessage {
- RcsOutgoingMessage(RcsControllerCall rcsControllerCall, int id) {
- super(rcsControllerCall, id);
- }
-
- /**
- * @return Returns the {@link RcsOutgoingMessageDelivery}s associated with this message. Please
- * note that the deliveries returned for the {@link RcsOutgoingMessage} may not always match the
- * {@link RcsParticipant}s on the {@link RcsGroupThread} as the group recipients may have
- * changed.
- * @throws RcsMessageStoreException if the outgoing deliveries could not be read from storage.
- */
- @NonNull
- @WorkerThread
- public List<RcsOutgoingMessageDelivery> getOutgoingDeliveries()
- throws RcsMessageStoreException {
- int[] deliveryParticipants;
- List<RcsOutgoingMessageDelivery> messageDeliveries = new ArrayList<>();
-
- deliveryParticipants = mRcsControllerCall.call(
- (iRcs, callingPackage) -> iRcs.getMessageRecipients(mId, callingPackage));
-
- if (deliveryParticipants != null) {
- for (Integer deliveryParticipant : deliveryParticipants) {
- messageDeliveries.add(new RcsOutgoingMessageDelivery(
- mRcsControllerCall, deliveryParticipant, mId));
- }
- }
-
- return messageDeliveries;
- }
-
- /**
- * @return Returns {@code false} as this is not an incoming message.
- */
- @Override
- public boolean isIncoming() {
- return false;
- }
-}
diff --git a/telephony/java/android/telephony/ims/RcsOutgoingMessageCreationParams.aidl b/telephony/java/android/telephony/ims/RcsOutgoingMessageCreationParams.aidl
deleted file mode 100644
index 0c38d9f..0000000
--- a/telephony/java/android/telephony/ims/RcsOutgoingMessageCreationParams.aidl
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- *
- * Copyright 2019, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.telephony.ims;
-
-parcelable RcsOutgoingMessageCreationParams;
diff --git a/telephony/java/android/telephony/ims/RcsOutgoingMessageCreationParams.java b/telephony/java/android/telephony/ims/RcsOutgoingMessageCreationParams.java
deleted file mode 100644
index c001ffb..0000000
--- a/telephony/java/android/telephony/ims/RcsOutgoingMessageCreationParams.java
+++ /dev/null
@@ -1,90 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.telephony.ims;
-
-import android.annotation.NonNull;
-import android.os.Parcel;
-import android.os.Parcelable;
-
-/**
- * {@link RcsOutgoingMessageCreationParams} is a collection of parameters that should be passed
- * into {@link RcsThread#addOutgoingMessage(RcsOutgoingMessageCreationParams)} to generate an
- * {@link RcsOutgoingMessage} on that {@link RcsThread}
- *
- * @hide
- */
-public final class RcsOutgoingMessageCreationParams extends RcsMessageCreationParams
- implements Parcelable {
- /**
- * A builder to instantiate and persist an {@link RcsOutgoingMessage}
- */
- public static class Builder extends RcsMessageCreationParams.Builder {
-
- /**
- * Creates a new {@link Builder} to create an instance of
- * {@link RcsOutgoingMessageCreationParams}.
- *
- * @param originationTimestamp The timestamp of {@link RcsMessage} creation. The origination
- * timestamp value in milliseconds passed after midnight,
- * January 1, 1970 UTC
- * @param subscriptionId The subscription ID that was used to send or receive this
- * {@link RcsMessage}
- * @see android.telephony.SubscriptionInfo#getSubscriptionId()
- */
- public Builder(long originationTimestamp, int subscriptionId) {
- super(originationTimestamp, subscriptionId);
- }
-
- /**
- * Creates configuration parameters for a new message.
- */
- public RcsOutgoingMessageCreationParams build() {
- return new RcsOutgoingMessageCreationParams(this);
- }
- }
-
- private RcsOutgoingMessageCreationParams(Builder builder) {
- super(builder);
- }
-
- private RcsOutgoingMessageCreationParams(Parcel in) {
- super(in);
- }
-
- public static final @NonNull Creator<RcsOutgoingMessageCreationParams> CREATOR =
- new Creator<RcsOutgoingMessageCreationParams>() {
- @Override
- public RcsOutgoingMessageCreationParams createFromParcel(Parcel in) {
- return new RcsOutgoingMessageCreationParams(in);
- }
-
- @Override
- public RcsOutgoingMessageCreationParams[] newArray(int size) {
- return new RcsOutgoingMessageCreationParams[size];
- }
- };
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public void writeToParcel(Parcel dest, int flags) {
- super.writeToParcel(dest);
- }
-}
diff --git a/telephony/java/android/telephony/ims/RcsOutgoingMessageDelivery.java b/telephony/java/android/telephony/ims/RcsOutgoingMessageDelivery.java
deleted file mode 100644
index df4a3e4..0000000
--- a/telephony/java/android/telephony/ims/RcsOutgoingMessageDelivery.java
+++ /dev/null
@@ -1,140 +0,0 @@
-/*
- * 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 android.telephony.ims;
-
-import android.annotation.NonNull;
-import android.annotation.WorkerThread;
-
-/**
- * This class holds the delivery information of an {@link RcsOutgoingMessage} for each
- * {@link RcsParticipant} that the message was intended for.
- *
- * @hide
- */
-public class RcsOutgoingMessageDelivery {
- private final RcsControllerCall mRcsControllerCall;
- // The participant that this delivery is intended for
- private final int mRecipientId;
- // The message this delivery is associated with
- private final int mRcsOutgoingMessageId;
-
- /**
- * Constructor to be used with RcsOutgoingMessage.getDelivery()
- *
- * @hide
- */
- RcsOutgoingMessageDelivery(
- RcsControllerCall rcsControllerCall, int recipientId, int messageId) {
- mRcsControllerCall = rcsControllerCall;
- mRecipientId = recipientId;
- mRcsOutgoingMessageId = messageId;
- }
-
- /**
- * Sets the delivery time of this outgoing delivery and persists into storage.
- *
- * @param deliveredTimestamp The timestamp to set to delivery. It is defined as milliseconds
- * passed after midnight, January 1, 1970 UTC
- * @throws RcsMessageStoreException if the value could not be persisted into storage
- */
- @WorkerThread
- public void setDeliveredTimestamp(long deliveredTimestamp) throws RcsMessageStoreException {
- mRcsControllerCall.callWithNoReturn(
- (iRcs, callingPackage) -> iRcs.setOutgoingDeliveryDeliveredTimestamp(
- mRcsOutgoingMessageId, mRecipientId, deliveredTimestamp, callingPackage));
- }
-
- /**
- * @return Returns the delivered timestamp of the associated message to the associated
- * participant. Timestamp is defined as milliseconds passed after midnight, January 1, 1970 UTC.
- * Returns 0 if the {@link RcsOutgoingMessage} is not delivered yet.
- * @throws RcsMessageStoreException if the value could not be read from the storage
- */
- @WorkerThread
- public long getDeliveredTimestamp() throws RcsMessageStoreException {
- return mRcsControllerCall.call(
- (iRcs, callingPackage) -> iRcs.getOutgoingDeliveryDeliveredTimestamp(
- mRcsOutgoingMessageId, mRecipientId, callingPackage));
- }
-
- /**
- * Sets the seen time of this outgoing delivery and persists into storage.
- *
- * @param seenTimestamp The timestamp to set to delivery. It is defined as milliseconds
- * passed after midnight, January 1, 1970 UTC
- * @throws RcsMessageStoreException if the value could not be persisted into storage
- */
- @WorkerThread
- public void setSeenTimestamp(long seenTimestamp) throws RcsMessageStoreException {
- mRcsControllerCall.callWithNoReturn(
- (iRcs, callingPackage) -> iRcs.setOutgoingDeliverySeenTimestamp(
- mRcsOutgoingMessageId, mRecipientId, seenTimestamp, callingPackage));
- }
-
- /**
- * @return Returns the seen timestamp of the associated message by the associated
- * participant. Timestamp is defined as milliseconds passed after midnight, January 1, 1970 UTC.
- * Returns 0 if the {@link RcsOutgoingMessage} is not seen yet.
- * @throws RcsMessageStoreException if the value could not be read from the storage
- */
- @WorkerThread
- public long getSeenTimestamp() throws RcsMessageStoreException {
- return mRcsControllerCall.call(
- (iRcs, callingPackage) -> iRcs.getOutgoingDeliverySeenTimestamp(
- mRcsOutgoingMessageId, mRecipientId, callingPackage));
- }
-
- /**
- * Sets the status of this outgoing delivery and persists into storage.
- *
- * @param status The status of the associated {@link RcsMessage}s delivery to the associated
- * {@link RcsParticipant}
- * @throws RcsMessageStoreException if the value could not be persisted into storage
- */
- @WorkerThread
- public void setStatus(@RcsMessage.RcsMessageStatus int status) throws RcsMessageStoreException {
- mRcsControllerCall.callWithNoReturn(
- (iRcs, callingPackage) -> iRcs.setOutgoingDeliveryStatus(
- mRcsOutgoingMessageId, mRecipientId, status, callingPackage));
- }
-
- /**
- * @return Returns the status of this outgoing delivery.
- * @throws RcsMessageStoreException if the value could not be read from the storage
- */
- @WorkerThread
- public @RcsMessage.RcsMessageStatus int getStatus() throws RcsMessageStoreException {
- return mRcsControllerCall.call(
- (iRcs, callingPackage) -> iRcs.getOutgoingDeliveryStatus(mRcsOutgoingMessageId,
- mRecipientId, callingPackage));
- }
-
- /**
- * @return Returns the recipient associated with this delivery.
- */
- @NonNull
- public RcsParticipant getRecipient() {
- return new RcsParticipant(mRcsControllerCall, mRecipientId);
- }
-
- /**
- * @return Returns the {@link RcsOutgoingMessage} associated with this delivery.
- */
- @NonNull
- public RcsOutgoingMessage getMessage() {
- return new RcsOutgoingMessage(mRcsControllerCall, mRcsOutgoingMessageId);
- }
-}
diff --git a/telephony/java/android/telephony/ims/RcsParticipant.java b/telephony/java/android/telephony/ims/RcsParticipant.java
deleted file mode 100644
index 8512e96..0000000
--- a/telephony/java/android/telephony/ims/RcsParticipant.java
+++ /dev/null
@@ -1,136 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.telephony.ims;
-
-import android.annotation.Nullable;
-import android.annotation.WorkerThread;
-
-/**
- * RcsParticipant is an RCS capable contact that can participate in {@link RcsThread}s.
- *
- * @hide
- */
-public class RcsParticipant {
- private final RcsControllerCall mRcsControllerCall;
- // The row ID of this participant in the database
- private final int mId;
-
- /**
- * Constructor for {@link com.android.internal.telephony.ims.RcsMessageStoreController}
- * to create instances of participants. This is not meant to be part of the SDK.
- *
- * @hide
- */
- public RcsParticipant(RcsControllerCall rcsControllerCall, int id) {
- mRcsControllerCall = rcsControllerCall;
- mId = id;
- }
-
- /**
- * @return Returns the canonical address (i.e. normalized phone number) for this
- * {@link RcsParticipant}
- * @throws RcsMessageStoreException if the value could not be read from the storage
- */
- @Nullable
- @WorkerThread
- public String getCanonicalAddress() throws RcsMessageStoreException {
- return mRcsControllerCall.call(
- (iRcs, callingPackage) -> iRcs.getRcsParticipantCanonicalAddress(mId,
- callingPackage));
- }
-
- /**
- * @return Returns the alias for this {@link RcsParticipant}. Alias is usually the real name of
- * the person themselves. Please see US5-15 - GSMA RCC.71 (RCS Universal Profile Service
- * Definition Document)
- * @throws RcsMessageStoreException if the value could not be read from the storage
- */
- @Nullable
- @WorkerThread
- public String getAlias() throws RcsMessageStoreException {
- return mRcsControllerCall.call(
- (iRcs, callingPackage) -> iRcs.getRcsParticipantAlias(mId, callingPackage));
- }
-
- /**
- * Sets the alias for this {@link RcsParticipant} and persists it in storage. Alias is usually
- * the real name of the person themselves. Please see US5-15 - GSMA RCC.71 (RCS Universal
- * Profile Service Definition Document)
- *
- * @param alias The alias to set to.
- * @throws RcsMessageStoreException if the value could not be persisted into storage
- */
- @WorkerThread
- public void setAlias(String alias) throws RcsMessageStoreException {
- mRcsControllerCall.callWithNoReturn(
- (iRcs, callingPackage) -> iRcs.setRcsParticipantAlias(mId, alias, callingPackage));
- }
-
- /**
- * @return Returns the contact ID for this {@link RcsParticipant}. Contact ID is a unique ID for
- * an {@link RcsParticipant} that is RCS provisioned. Please see 4.4.5 - GSMA RCC.53 (RCS Device
- * API 1.6 Specification)
- * @throws RcsMessageStoreException if the value could not be read from the storage
- */
- @Nullable
- @WorkerThread
- public String getContactId() throws RcsMessageStoreException {
- return mRcsControllerCall.call(
- (iRcs, callingPackage) -> iRcs.getRcsParticipantContactId(mId, callingPackage));
- }
-
- /**
- * Sets the contact ID for this {@link RcsParticipant}. Contact ID is a unique ID for
- * an {@link RcsParticipant} that is RCS provisioned. Please see 4.4.5 - GSMA RCC.53 (RCS Device
- * API 1.6 Specification)
- *
- * @param contactId The contact ID to set to.
- * @throws RcsMessageStoreException if the value could not be persisted into storage
- */
- @WorkerThread
- public void setContactId(String contactId) throws RcsMessageStoreException {
- mRcsControllerCall.callWithNoReturn(
- (iRcs, callingPackage) -> iRcs.setRcsParticipantContactId(mId, contactId,
- callingPackage));
- }
-
- @Override
- public boolean equals(Object obj) {
- if (this == obj) {
- return true;
- }
- if (!(obj instanceof RcsParticipant)) {
- return false;
- }
- RcsParticipant other = (RcsParticipant) obj;
-
- return mId == other.mId;
- }
-
- @Override
- public int hashCode() {
- return mId;
- }
-
- /**
- * Returns the row id of this participant. This is not meant to be part of the SDK
- *
- * @hide
- */
- public int getId() {
- return mId;
- }
-}
diff --git a/telephony/java/android/telephony/ims/RcsParticipantAliasChangedEvent.java b/telephony/java/android/telephony/ims/RcsParticipantAliasChangedEvent.java
deleted file mode 100644
index 865bc05..0000000
--- a/telephony/java/android/telephony/ims/RcsParticipantAliasChangedEvent.java
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.telephony.ims;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-
-/**
- * An event that indicates an {@link RcsParticipant}'s alias was changed. Please see US18-2 - GSMA
- * RCC.71 (RCS Universal Profile Service Definition Document)
- *
- * @hide
- */
-public final class RcsParticipantAliasChangedEvent extends RcsEvent {
- // The participant that changed their alias
- private final RcsParticipant mParticipant;
- // The new alias of the above participant
- private final String mNewAlias;
-
- /**
- * Creates a new {@link RcsParticipantAliasChangedEvent}. This event is not persisted into
- * storage until {@link RcsMessageStore#persistRcsEvent(RcsEvent)} is called.
- *
- * @param timestamp The timestamp of when this event happened, in milliseconds passed after
- * midnight, January 1st, 1970 UTC
- * @param participant The {@link RcsParticipant} that got their alias changed
- * @param newAlias The new alias the {@link RcsParticipant} has.
- * @see RcsMessageStore#persistRcsEvent(RcsEvent)
- */
- public RcsParticipantAliasChangedEvent(long timestamp, @NonNull RcsParticipant participant,
- @Nullable String newAlias) {
- super(timestamp);
- mParticipant = participant;
- mNewAlias = newAlias;
- }
-
- /**
- * @return Returns the {@link RcsParticipant} whose alias was changed.
- */
- @NonNull
- public RcsParticipant getParticipant() {
- return mParticipant;
- }
-
- /**
- * @return Returns the alias of the associated {@link RcsParticipant} after this event happened
- */
- @Nullable
- public String getNewAlias() {
- return mNewAlias;
- }
-
- /**
- * Persists the event to the data store.
- *
- * @hide - not meant for public use.
- */
- @Override
- void persist(RcsControllerCall rcsControllerCall) throws RcsMessageStoreException {
- rcsControllerCall.call((iRcs, callingPackage) -> iRcs.createParticipantAliasChangedEvent(
- getTimestamp(), getParticipant().getId(), getNewAlias(), callingPackage));
- }
-}
diff --git a/telephony/java/android/telephony/ims/RcsParticipantAliasChangedEventDescriptor.aidl b/telephony/java/android/telephony/ims/RcsParticipantAliasChangedEventDescriptor.aidl
deleted file mode 100644
index 64fe3b8..0000000
--- a/telephony/java/android/telephony/ims/RcsParticipantAliasChangedEventDescriptor.aidl
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- *
- * Copyright 2019, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.telephony.ims;
-
-parcelable RcsParticipantAliasChangedEventDescriptor;
diff --git a/telephony/java/android/telephony/ims/RcsParticipantAliasChangedEventDescriptor.java b/telephony/java/android/telephony/ims/RcsParticipantAliasChangedEventDescriptor.java
deleted file mode 100644
index 43b918c..0000000
--- a/telephony/java/android/telephony/ims/RcsParticipantAliasChangedEventDescriptor.java
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.telephony.ims;
-
-import static com.android.internal.annotations.VisibleForTesting.Visibility.PROTECTED;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.os.Parcel;
-
-import com.android.internal.annotations.VisibleForTesting;
-
-/**
- * @hide - used only for internal communication with the ircs service
- */
-public class RcsParticipantAliasChangedEventDescriptor extends RcsEventDescriptor {
- // The ID of the participant that changed their alias
- protected int mParticipantId;
- // The new alias of the above participant
- protected String mNewAlias;
-
- public RcsParticipantAliasChangedEventDescriptor(long timestamp, int participantId,
- @Nullable String newAlias) {
- super(timestamp);
- mParticipantId = participantId;
- mNewAlias = newAlias;
- }
-
- @Override
- @VisibleForTesting(visibility = PROTECTED)
- public RcsParticipantAliasChangedEvent createRcsEvent(RcsControllerCall rcsControllerCall) {
- return new RcsParticipantAliasChangedEvent(
- mTimestamp, new RcsParticipant(rcsControllerCall, mParticipantId), mNewAlias);
- }
-
- public static final @NonNull Creator<RcsParticipantAliasChangedEventDescriptor> CREATOR =
- new Creator<RcsParticipantAliasChangedEventDescriptor>() {
- @Override
- public RcsParticipantAliasChangedEventDescriptor createFromParcel(Parcel in) {
- return new RcsParticipantAliasChangedEventDescriptor(in);
- }
-
- @Override
- public RcsParticipantAliasChangedEventDescriptor[] newArray(int size) {
- return new RcsParticipantAliasChangedEventDescriptor[size];
- }
- };
-
- protected RcsParticipantAliasChangedEventDescriptor(Parcel in) {
- super(in);
- mNewAlias = in.readString();
- mParticipantId = in.readInt();
- }
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public void writeToParcel(Parcel dest, int flags) {
- super.writeToParcel(dest, flags);
- dest.writeString(mNewAlias);
- dest.writeInt(mParticipantId);
- }
-}
diff --git a/telephony/java/android/telephony/ims/RcsParticipantQueryParams.aidl b/telephony/java/android/telephony/ims/RcsParticipantQueryParams.aidl
deleted file mode 100644
index b7c0f93..0000000
--- a/telephony/java/android/telephony/ims/RcsParticipantQueryParams.aidl
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- *
- * Copyright 2019, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.telephony.ims;
-
-parcelable RcsParticipantQueryParams;
diff --git a/telephony/java/android/telephony/ims/RcsParticipantQueryParams.java b/telephony/java/android/telephony/ims/RcsParticipantQueryParams.java
deleted file mode 100644
index 21107a2..0000000
--- a/telephony/java/android/telephony/ims/RcsParticipantQueryParams.java
+++ /dev/null
@@ -1,310 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.telephony.ims;
-
-import android.annotation.CheckResult;
-import android.annotation.IntDef;
-import android.annotation.IntRange;
-import android.os.Parcel;
-import android.os.Parcelable;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.security.InvalidParameterException;
-
-/**
- * The parameters to pass into
- * {@link RcsMessageStore#getRcsParticipants(RcsParticipantQueryParams)} in order to select a
- * subset of {@link RcsThread}s present in the message store.
- *
- * @hide
- */
-public final class RcsParticipantQueryParams implements Parcelable {
- /**
- * Flag to set with {@link Builder#setSortProperty(int)} to sort the results in the order of
- * creation time for faster query results
- */
- public static final int SORT_BY_CREATION_ORDER = 0;
-
- /**
- * Flag to set with {@link Builder#setSortProperty(int)} to sort depending on the
- * {@link RcsParticipant} aliases
- */
- public static final int SORT_BY_ALIAS = 1;
-
- /**
- * Flag to set with {@link Builder#setSortProperty(int)} to sort depending on the
- * {@link RcsParticipant} canonical addresses
- */
- public static final int SORT_BY_CANONICAL_ADDRESS = 2;
-
- @Retention(RetentionPolicy.SOURCE)
- @IntDef({SORT_BY_CREATION_ORDER, SORT_BY_ALIAS, SORT_BY_CANONICAL_ADDRESS})
- public @interface SortingProperty {
- }
-
- // The SQL "like" statement to filter against participant aliases
- private String mAliasLike;
- // The SQL "like" statement to filter against canonical addresses
- private String mCanonicalAddressLike;
- // The property to sort the result against
- private @SortingProperty int mSortingProperty;
- // Whether to sort the result in ascending order
- private boolean mIsAscending;
- // The number of results to be returned from the query
- private int mLimit;
- // Used to limit the results to participants of a single thread
- private int mThreadId;
-
- /**
- * @hide
- */
- public static final String PARTICIPANT_QUERY_PARAMETERS_KEY = "participant_query_parameters";
-
- RcsParticipantQueryParams(int rcsThreadId, String aliasLike, String canonicalAddressLike,
- @SortingProperty int sortingProperty, boolean isAscending,
- int limit) {
- mThreadId = rcsThreadId;
- mAliasLike = aliasLike;
- mCanonicalAddressLike = canonicalAddressLike;
- mSortingProperty = sortingProperty;
- mIsAscending = isAscending;
- mLimit = limit;
- }
-
- /**
- * This is used in {@link com.android.internal.telephony.ims.RcsMessageStoreController} to get
- * the thread that the result query should be limited to.
- *
- * As we do not expose any sort of integer ID's to public usage, this should be hidden.
- *
- * @hide - not meant for public use
- */
- public int getThreadId() {
- return mThreadId;
- }
-
- /**
- * @return Returns the SQL-inspired "LIKE" clause that will be used to match
- * {@link RcsParticipant}s with respect to their aliases
- *
- * @see RcsParticipant#getAlias()
- */
- public String getAliasLike() {
- return mAliasLike;
- }
-
- /**
- * @return Returns the SQL-inspired "LIKE" clause that will be used to match
- * {@link RcsParticipant}s with respect to their canonical addresses.
- *
- * @see RcsParticipant#getCanonicalAddress()
- */
- public String getCanonicalAddressLike() {
- return mCanonicalAddressLike;
- }
-
- /**
- * @return Returns the number of {@link RcsParticipant}s to be returned from the query. A value
- * of 0 means there is no set limit.
- */
- public int getLimit() {
- return mLimit;
- }
-
- /**
- * @return Returns the property that will be used to sort the result against.
- * @see SortingProperty
- */
- public int getSortingProperty() {
- return mSortingProperty;
- }
-
- /**
- * @return Returns {@code true} if the result set will be sorted in ascending order,
- * {@code false} if it will be sorted in descending order.
- */
- public boolean getSortDirection() {
- return mIsAscending;
- }
-
- /**
- * A helper class to build the {@link RcsParticipantQueryParams}.
- */
- public static class Builder {
- private String mAliasLike;
- private String mCanonicalAddressLike;
- private @SortingProperty int mSortingProperty;
- private boolean mIsAscending;
- private int mLimit = 100;
- private int mThreadId;
-
- /**
- * Creates a new builder for {@link RcsParticipantQueryParams} to be used in
- * {@link RcsMessageStore#getRcsParticipants(RcsParticipantQueryParams)}
- */
- public Builder() {
- // empty implementation
- }
-
- /**
- * Limits the resulting {@link RcsParticipant}s to only the given {@link RcsThread}
- *
- * @param rcsThread The thread that the participants should be searched in.
- * @return The same {@link Builder} to chain methods.
- */
- @CheckResult
- public Builder setThread(RcsThread rcsThread) {
- mThreadId = rcsThread.getThreadId();
- return this;
- }
-
- /**
- * Sets an SQL-inspired "like" clause to match with participant aliases. Using a percent
- * sign ('%') wildcard matches any sequence of zero or more characters. Using an underscore
- * ('_') wildcard matches any single character. Not using any wildcards would only perform a
- * string match.The input string is case-insensitive.
- *
- * The input "An%e" would match {@link RcsParticipant}s with names Anne, Annie, Antonie,
- * while the input "An_e" would only match Anne.
- *
- * @param likeClause The like clause to use for matching {@link RcsParticipant} aliases.
- * @return The same {@link Builder} to chain methods
- */
- @CheckResult
- public Builder setAliasLike(String likeClause) {
- mAliasLike = likeClause;
- return this;
- }
-
- /**
- * Sets an SQL-inspired "like" clause to match with participant addresses. Using a percent
- * sign ('%') wildcard matches any sequence of zero or more characters. Using an underscore
- * ('_') wildcard matches any single character. Not using any wildcards would only perform a
- * string match. The input string is case-insensitive.
- *
- * The input "+999%111" would match {@link RcsParticipant}s with addresses like "+9995111"
- * or "+99955555111", while the input "+999_111" would only match "+9995111".
- *
- * @param likeClause The like clause to use for matching {@link RcsParticipant} canonical
- * addresses.
- * @return The same {@link Builder} to chain methods
- */
- @CheckResult
- public Builder setCanonicalAddressLike(String likeClause) {
- mCanonicalAddressLike = likeClause;
- return this;
- }
-
- /**
- * Desired number of threads to be returned from the query. Passing in 0 will return all
- * existing threads at once. The limit defaults to 100.
- *
- * @param limit The number to limit the query result to.
- * @return The same instance of the builder to chain parameters.
- * @throws InvalidParameterException If the given limit is negative.
- */
- @CheckResult
- public Builder setResultLimit(@IntRange(from = 0) int limit)
- throws InvalidParameterException {
- if (limit < 0) {
- throw new InvalidParameterException("The query limit must be non-negative");
- }
-
- mLimit = limit;
- return this;
- }
-
- /**
- * Sets the property where the results should be sorted against. Defaults to
- * {@link RcsParticipantQueryParams.SortingProperty#SORT_BY_CREATION_ORDER}
- *
- * @param sortingProperty against which property the results should be sorted
- * @return The same instance of the builder to chain parameters.
- */
- @CheckResult
- public Builder setSortProperty(@SortingProperty int sortingProperty) {
- mSortingProperty = sortingProperty;
- return this;
- }
-
- /**
- * Sets whether the results should be sorted ascending or descending
- *
- * @param isAscending whether the results should be sorted ascending
- * @return The same instance of the builder to chain parameters.
- */
- @CheckResult
- public Builder setSortDirection(boolean isAscending) {
- mIsAscending = isAscending;
- return this;
- }
-
- /**
- * Builds the {@link RcsParticipantQueryParams} to use in
- * {@link RcsMessageStore#getRcsParticipants(RcsParticipantQueryParams)}
- *
- * @return An instance of {@link RcsParticipantQueryParams} to use with the participant
- * query.
- */
- public RcsParticipantQueryParams build() {
- return new RcsParticipantQueryParams(mThreadId, mAliasLike, mCanonicalAddressLike,
- mSortingProperty, mIsAscending, mLimit);
- }
- }
-
- /**
- * Parcelable boilerplate below.
- */
- private RcsParticipantQueryParams(Parcel in) {
- mAliasLike = in.readString();
- mCanonicalAddressLike = in.readString();
- mSortingProperty = in.readInt();
- mIsAscending = in.readByte() == 1;
- mLimit = in.readInt();
- mThreadId = in.readInt();
- }
-
- public static final @android.annotation.NonNull Creator<RcsParticipantQueryParams> CREATOR =
- new Creator<RcsParticipantQueryParams>() {
- @Override
- public RcsParticipantQueryParams createFromParcel(Parcel in) {
- return new RcsParticipantQueryParams(in);
- }
-
- @Override
- public RcsParticipantQueryParams[] newArray(int size) {
- return new RcsParticipantQueryParams[size];
- }
- };
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public void writeToParcel(Parcel dest, int flags) {
- dest.writeString(mAliasLike);
- dest.writeString(mCanonicalAddressLike);
- dest.writeInt(mSortingProperty);
- dest.writeByte((byte) (mIsAscending ? 1 : 0));
- dest.writeInt(mLimit);
- dest.writeInt(mThreadId);
- }
-
-}
diff --git a/telephony/java/android/telephony/ims/RcsParticipantQueryResult.java b/telephony/java/android/telephony/ims/RcsParticipantQueryResult.java
deleted file mode 100644
index 0721dfd..0000000
--- a/telephony/java/android/telephony/ims/RcsParticipantQueryResult.java
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.telephony.ims;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-
-import java.util.List;
-import java.util.stream.Collectors;
-
-/**
- * The result of a {@link RcsMessageStore#getRcsParticipants(RcsParticipantQueryParams)}
- * call. This class allows getting the token for querying the next batch of participants in order to
- * prevent handling large amounts of data at once.
- *
- * @hide
- */
-public final class RcsParticipantQueryResult {
- private final RcsControllerCall mRcsControllerCall;
- private final RcsParticipantQueryResultParcelable mRcsParticipantQueryResultParcelable;
-
- RcsParticipantQueryResult(
- RcsControllerCall rcsControllerCall,
- RcsParticipantQueryResultParcelable rcsParticipantQueryResultParcelable) {
- mRcsControllerCall = rcsControllerCall;
- mRcsParticipantQueryResultParcelable = rcsParticipantQueryResultParcelable;
- }
-
- /**
- * Returns a token to call
- * {@link RcsMessageStore#getRcsParticipants(RcsQueryContinuationToken)}
- * to get the next batch of {@link RcsParticipant}s.
- */
- @Nullable
- public RcsQueryContinuationToken getContinuationToken() {
- return mRcsParticipantQueryResultParcelable.mContinuationToken;
- }
-
- /**
- * Returns all the {@link RcsParticipant}s in the current query result. Call {@link
- * RcsMessageStore#getRcsParticipants(RcsQueryContinuationToken)} to get the next
- * batch of {@link RcsParticipant}s.
- */
- @NonNull
- public List<RcsParticipant> getParticipants() {
- return mRcsParticipantQueryResultParcelable.mParticipantIds.stream()
- .map(participantId -> new RcsParticipant(mRcsControllerCall, participantId))
- .collect(Collectors.toList());
- }
-}
diff --git a/telephony/java/android/telephony/ims/RcsParticipantQueryResultParcelable.aidl b/telephony/java/android/telephony/ims/RcsParticipantQueryResultParcelable.aidl
deleted file mode 100644
index 54c72e7..0000000
--- a/telephony/java/android/telephony/ims/RcsParticipantQueryResultParcelable.aidl
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- *
- * Copyright 2019, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.telephony.ims;
-
-parcelable RcsParticipantQueryResultParcelable;
diff --git a/telephony/java/android/telephony/ims/RcsParticipantQueryResultParcelable.java b/telephony/java/android/telephony/ims/RcsParticipantQueryResultParcelable.java
deleted file mode 100644
index 239b0e9..0000000
--- a/telephony/java/android/telephony/ims/RcsParticipantQueryResultParcelable.java
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.telephony.ims;
-
-import android.os.Parcel;
-import android.os.Parcelable;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * @hide
- */
-public final class RcsParticipantQueryResultParcelable implements Parcelable {
- final RcsQueryContinuationToken mContinuationToken;
- final List<Integer> mParticipantIds;
-
- public RcsParticipantQueryResultParcelable(
- RcsQueryContinuationToken continuationToken,
- List<Integer> participantIds) {
- mContinuationToken = continuationToken;
- mParticipantIds = participantIds;
- }
-
- private RcsParticipantQueryResultParcelable(Parcel in) {
- mContinuationToken = in.readParcelable(RcsQueryContinuationToken.class.getClassLoader());
- mParticipantIds = new ArrayList<>();
- in.readList(mParticipantIds, Integer.class.getClassLoader());
- }
-
- public static final Parcelable.Creator<RcsParticipantQueryResultParcelable> CREATOR =
- new Parcelable.Creator<RcsParticipantQueryResultParcelable>() {
- @Override
- public RcsParticipantQueryResultParcelable createFromParcel(Parcel in) {
- return new RcsParticipantQueryResultParcelable(in);
- }
-
- @Override
- public RcsParticipantQueryResultParcelable[] newArray(int size) {
- return new RcsParticipantQueryResultParcelable[size];
- }
- };
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public void writeToParcel(Parcel dest, int flags) {
- dest.writeParcelable(mContinuationToken, flags);
- dest.writeList(mParticipantIds);
- }
-}
diff --git a/telephony/java/android/telephony/ims/RcsQueryContinuationToken.aidl b/telephony/java/android/telephony/ims/RcsQueryContinuationToken.aidl
deleted file mode 100644
index 319379a..0000000
--- a/telephony/java/android/telephony/ims/RcsQueryContinuationToken.aidl
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- *
- * Copyright 2019, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.telephony.ims;
-
-parcelable RcsQueryContinuationToken;
diff --git a/telephony/java/android/telephony/ims/RcsQueryContinuationToken.java b/telephony/java/android/telephony/ims/RcsQueryContinuationToken.java
deleted file mode 100644
index 982263a..0000000
--- a/telephony/java/android/telephony/ims/RcsQueryContinuationToken.java
+++ /dev/null
@@ -1,157 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.telephony.ims;
-
-import android.annotation.IntDef;
-import android.os.Parcel;
-import android.os.Parcelable;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-
-/**
- * A token for enabling continuation queries. Instances are acquired through
- * {@code getContinuationToken} on result objects after initial query is done.
- *
- * @see RcsEventQueryResult#getContinuationToken()
- * @see RcsMessageQueryResult#getContinuationToken()
- * @see RcsParticipantQueryResult#getContinuationToken()
- * @see RcsThreadQueryResult#getContinuationToken()
- *
- * @hide
- */
-public final class RcsQueryContinuationToken implements Parcelable {
- /**
- * Denotes that this {@link RcsQueryContinuationToken} token is meant to allow continuing
- * {@link RcsEvent} queries
- */
- public static final int EVENT_QUERY_CONTINUATION_TOKEN_TYPE = 0;
-
- /**
- * Denotes that this {@link RcsQueryContinuationToken} token is meant to allow continuing
- * {@link RcsMessage} queries
- */
- public static final int MESSAGE_QUERY_CONTINUATION_TOKEN_TYPE = 1;
-
- /**
- * Denotes that this {@link RcsQueryContinuationToken} token is meant to allow continuing
- * {@link RcsParticipant} queries
- */
- public static final int PARTICIPANT_QUERY_CONTINUATION_TOKEN_TYPE = 2;
-
- /**
- * Denotes that this {@link RcsQueryContinuationToken} token is meant to allow continuing
- * {@link RcsThread} queries
- */
- public static final int THREAD_QUERY_CONTINUATION_TOKEN_TYPE = 3;
-
- /**
- * @hide - not meant for public use
- */
- public static final String QUERY_CONTINUATION_TOKEN = "query_continuation_token";
-
- @Retention(RetentionPolicy.SOURCE)
- @IntDef({EVENT_QUERY_CONTINUATION_TOKEN_TYPE, MESSAGE_QUERY_CONTINUATION_TOKEN_TYPE,
- PARTICIPANT_QUERY_CONTINUATION_TOKEN_TYPE, THREAD_QUERY_CONTINUATION_TOKEN_TYPE})
- public @interface ContinuationTokenType {}
-
- // The type of query this token should allow to continue
- private @ContinuationTokenType int mQueryType;
- // The raw query string for the initial query
- private final String mRawQuery;
- // The number of results that is returned with each query
- private final int mLimit;
- // The offset value that this query should start the query from
- private int mOffset;
-
- /**
- * @hide
- */
- public RcsQueryContinuationToken(@ContinuationTokenType int queryType, String rawQuery,
- int limit, int offset) {
- mQueryType = queryType;
- mRawQuery = rawQuery;
- mLimit = limit;
- mOffset = offset;
- }
-
- /**
- * Returns the original raw query used on {@link com.android.providers.telephony.RcsProvider}
- * @hide
- */
- public String getRawQuery() {
- return mRawQuery;
- }
-
- /**
- * Returns which index this continuation query should start from
- * @hide
- */
- public int getOffset() {
- return mOffset;
- }
-
- /**
- * Increments the offset by the amount of result rows returned with the continuation query for
- * the next query.
- * @hide
- */
- public void incrementOffset() {
- mOffset += mLimit;
- }
-
- /**
- * Returns the type of query that this {@link RcsQueryContinuationToken} is intended to be used
- * to continue.
- */
- public @ContinuationTokenType int getQueryType() {
- return mQueryType;
- }
-
- private RcsQueryContinuationToken(Parcel in) {
- mQueryType = in.readInt();
- mRawQuery = in.readString();
- mLimit = in.readInt();
- mOffset = in.readInt();
- }
-
- public static final @android.annotation.NonNull Creator<RcsQueryContinuationToken> CREATOR =
- new Creator<RcsQueryContinuationToken>() {
- @Override
- public RcsQueryContinuationToken createFromParcel(Parcel in) {
- return new RcsQueryContinuationToken(in);
- }
-
- @Override
- public RcsQueryContinuationToken[] newArray(int size) {
- return new RcsQueryContinuationToken[size];
- }
- };
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public void writeToParcel(Parcel dest, int flags) {
- dest.writeInt(mQueryType);
- dest.writeString(mRawQuery);
- dest.writeInt(mLimit);
- dest.writeInt(mOffset);
- }
-}
diff --git a/telephony/java/android/telephony/ims/RcsThread.java b/telephony/java/android/telephony/ims/RcsThread.java
deleted file mode 100644
index efb2cca..0000000
--- a/telephony/java/android/telephony/ims/RcsThread.java
+++ /dev/null
@@ -1,148 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.telephony.ims;
-
-import static android.provider.Telephony.RcsColumns.RcsUnifiedThreadColumns.THREAD_TYPE_1_TO_1;
-import static android.provider.Telephony.RcsColumns.RcsUnifiedThreadColumns.THREAD_TYPE_GROUP;
-
-import android.annotation.NonNull;
-import android.annotation.WorkerThread;
-
-import com.android.internal.annotations.VisibleForTesting;
-
-/**
- * RcsThread represents a single RCS conversation thread. It holds messages that were sent and
- * received and events that occurred on that thread.
- *
- * @hide
- */
-public abstract class RcsThread {
- /**
- * The rcs_participant_thread_id that represents this thread in the database
- *
- * @hide
- */
- protected int mThreadId;
-
- /**
- * @hide
- */
- protected final RcsControllerCall mRcsControllerCall;
-
- /**
- * @hide
- */
- protected RcsThread(RcsControllerCall rcsControllerCall, int threadId) {
- mThreadId = threadId;
- mRcsControllerCall = rcsControllerCall;
- }
-
- /**
- * @return Returns the summary of the latest message in this {@link RcsThread} packaged in an
- * {@link RcsMessageSnippet} object
- */
- @WorkerThread
- @NonNull
- public RcsMessageSnippet getSnippet() throws RcsMessageStoreException {
- return mRcsControllerCall.call(
- (iRcs, callingPackage) -> iRcs.getMessageSnippet(mThreadId, callingPackage));
- }
-
- /**
- * Adds a new {@link RcsIncomingMessage} to this RcsThread and persists it in storage.
- *
- * @throws RcsMessageStoreException if the message could not be persisted into storage.
- */
- @WorkerThread
- @NonNull
- public RcsIncomingMessage addIncomingMessage(
- @NonNull RcsIncomingMessageCreationParams rcsIncomingMessageCreationParams)
- throws RcsMessageStoreException {
- int messageId = mRcsControllerCall.call(
- (iRcs, callingPackage) -> iRcs.addIncomingMessage(mThreadId,
- rcsIncomingMessageCreationParams, callingPackage));
- return new RcsIncomingMessage(mRcsControllerCall, messageId);
- }
-
- /**
- * Adds a new {@link RcsOutgoingMessage} to this RcsThread and persists it in storage.
- *
- * @throws RcsMessageStoreException if the message could not be persisted into storage.
- */
- @WorkerThread
- @NonNull
- public RcsOutgoingMessage addOutgoingMessage(
- @NonNull RcsOutgoingMessageCreationParams rcsOutgoingMessageCreationParams)
- throws RcsMessageStoreException {
- int messageId = mRcsControllerCall.call((iRcs, callingPackage) -> iRcs.addOutgoingMessage(
- mThreadId, rcsOutgoingMessageCreationParams, callingPackage));
-
- return new RcsOutgoingMessage(mRcsControllerCall, messageId);
- }
-
- /**
- * Deletes an {@link RcsMessage} from this RcsThread and updates the storage.
- *
- * @param rcsMessage The message to delete from the thread
- * @throws RcsMessageStoreException if the message could not be deleted
- */
- @WorkerThread
- public void deleteMessage(@NonNull RcsMessage rcsMessage) throws RcsMessageStoreException {
- mRcsControllerCall.callWithNoReturn(
- (iRcs, callingPackage) -> iRcs.deleteMessage(rcsMessage.getId(),
- rcsMessage.isIncoming(), mThreadId,
- isGroup(), callingPackage));
- }
-
- /**
- * Convenience function for loading all the {@link RcsMessage}s in this {@link RcsThread}. For
- * a more detailed and paginated query, please use
- * {@link RcsMessageStore#getRcsMessages(RcsMessageQueryParams)}
- *
- * @return Loads the {@link RcsMessage}s in this thread and returns them in an immutable list.
- * @throws RcsMessageStoreException if the messages could not be read from the storage
- */
- @WorkerThread
- @NonNull
- public RcsMessageQueryResult getMessages() throws RcsMessageStoreException {
- RcsMessageQueryParams queryParams =
- new RcsMessageQueryParams.Builder().setThread(this).build();
- return new RcsMessageQueryResult(mRcsControllerCall,
- mRcsControllerCall.call(
- (iRcs, callingPackage) -> iRcs.getMessages(queryParams, callingPackage)));
- }
-
- /**
- * @return Returns whether this is a group thread or not
- */
- public abstract boolean isGroup();
-
- /**
- * @hide
- */
- @VisibleForTesting
- public int getThreadId() {
- return mThreadId;
- }
-
- /**
- * @hide
- */
- public int getThreadType() {
- return isGroup() ? THREAD_TYPE_GROUP : THREAD_TYPE_1_TO_1;
- }
-}
diff --git a/telephony/java/android/telephony/ims/RcsThreadQueryParams.aidl b/telephony/java/android/telephony/ims/RcsThreadQueryParams.aidl
deleted file mode 100644
index 3f351dc..0000000
--- a/telephony/java/android/telephony/ims/RcsThreadQueryParams.aidl
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- *
- * Copyright 2019, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.telephony.ims;
-
-parcelable RcsThreadQueryParams;
diff --git a/telephony/java/android/telephony/ims/RcsThreadQueryParams.java b/telephony/java/android/telephony/ims/RcsThreadQueryParams.java
deleted file mode 100644
index da7cdb0..0000000
--- a/telephony/java/android/telephony/ims/RcsThreadQueryParams.java
+++ /dev/null
@@ -1,305 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.telephony.ims;
-
-import android.annotation.CheckResult;
-import android.annotation.IntDef;
-import android.annotation.IntRange;
-import android.annotation.NonNull;
-import android.os.Parcel;
-import android.os.Parcelable;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.security.InvalidParameterException;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-
-/**
- * The parameters to pass into {@link RcsMessageStore#getRcsThreads(RcsThreadQueryParams)} in
- * order to select a subset of {@link RcsThread}s present in the message store.
- *
- * @hide
- */
-public final class RcsThreadQueryParams implements Parcelable {
- /**
- * Bitmask flag to be used with {@link Builder#setThreadType(int)} to make
- * {@link RcsMessageStore#getRcsThreads(RcsThreadQueryParams)} return
- * {@link RcsGroupThread}s.
- */
- public static final int THREAD_TYPE_GROUP = 0x0001;
-
- /**
- * Bitmask flag to be used with {@link Builder#setThreadType(int)} to make
- * {@link RcsMessageStore#getRcsThreads(RcsThreadQueryParams)} return
- * {@link Rcs1To1Thread}s.
- */
- public static final int THREAD_TYPE_1_TO_1 = 0x0002;
-
- // The type of threads to be filtered with the query
- private final int mThreadType;
- // The list of participants that are expected in the resulting threads
- private final List<Integer> mRcsParticipantIds;
- // The number of RcsThread's that should be returned with this query
- private final int mLimit;
- // The property which the result of the query should be sorted against
- private final @SortingProperty int mSortingProperty;
- // Whether the sorting should be done in ascending
- private final boolean mIsAscending;
-
- /**
- * Flag to be used with {@link Builder#setSortProperty(int)} to denote that the results should
- * be sorted in the order of {@link RcsThread} creation time for faster results.
- */
- public static final int SORT_BY_CREATION_ORDER = 0;
-
- /**
- * Flag to be used with {@link Builder#setSortProperty(int)} to denote that the results should
- * be sorted according to the timestamp of {@link RcsThread#getSnippet()}
- */
- public static final int SORT_BY_TIMESTAMP = 1;
-
- @Retention(RetentionPolicy.SOURCE)
- @IntDef({SORT_BY_CREATION_ORDER, SORT_BY_TIMESTAMP})
- public @interface SortingProperty {
- }
-
- /**
- * @hide
- */
- public static final String THREAD_QUERY_PARAMETERS_KEY = "thread_query_parameters";
-
- RcsThreadQueryParams(int threadType, Set<RcsParticipant> participants,
- int limit, int sortingProperty, boolean isAscending) {
- mThreadType = threadType;
- mRcsParticipantIds = convertParticipantSetToIdList(participants);
- mLimit = limit;
- mSortingProperty = sortingProperty;
- mIsAscending = isAscending;
- }
-
- private static List<Integer> convertParticipantSetToIdList(Set<RcsParticipant> participants) {
- List<Integer> ids = new ArrayList<>(participants.size());
- for (RcsParticipant participant : participants) {
- ids.add(participant.getId());
- }
- return ids;
- }
-
- /**
- * This is used in {@link com.android.internal.telephony.ims.RcsMessageStoreController} to get
- * the list of participant IDs.
- *
- * As we don't expose any integer ID's to API users, this should stay hidden
- *
- * @hide - not meant for public use
- */
- public List<Integer> getRcsParticipantsIds() {
- return Collections.unmodifiableList(mRcsParticipantIds);
- }
-
- /**
- * @return Returns the bitmask flag for types of {@link RcsThread}s that this query should
- * return.
- */
- public int getThreadType() {
- return mThreadType;
- }
-
- /**
- * @return Returns the number of {@link RcsThread}s to be returned from the query. A value
- * of 0 means there is no set limit.
- */
- public int getLimit() {
- return mLimit;
- }
-
- /**
- * @return Returns the property that will be used to sort the result against.
- * @see SortingProperty
- */
- public @SortingProperty int getSortingProperty() {
- return mSortingProperty;
- }
-
- /**
- * @return Returns {@code true} if the result set will be sorted in ascending order,
- * {@code false} if it will be sorted in descending order.
- */
- public boolean getSortDirection() {
- return mIsAscending;
- }
-
- /**
- * A helper class to build the {@link RcsThreadQueryParams}.
- */
- public static class Builder {
- private int mThreadType;
- private Set<RcsParticipant> mParticipants;
- private int mLimit = 100;
- private @SortingProperty int mSortingProperty;
- private boolean mIsAscending;
-
- /**
- * Constructs a {@link RcsThreadQueryParams.Builder} to help build an
- * {@link RcsThreadQueryParams}
- */
- public Builder() {
- mParticipants = new HashSet<>();
- }
-
- /**
- * Limits the query to only return group threads.
- *
- * @param threadType Whether to limit the query result to group threads.
- * @return The same instance of the builder to chain parameters.
- * @see RcsThreadQueryParams#THREAD_TYPE_GROUP
- * @see RcsThreadQueryParams#THREAD_TYPE_1_TO_1
- */
- @CheckResult
- public Builder setThreadType(int threadType) {
- mThreadType = threadType;
- return this;
- }
-
- /**
- * Limits the query to only return threads that contain the given participant. If this
- * property was not set, participants will not be taken into account while querying for
- * threads.
- *
- * @param participant The participant that must be included in all of the returned threads.
- * @return The same instance of the builder to chain parameters.
- */
- @CheckResult
- public Builder setParticipant(@NonNull RcsParticipant participant) {
- mParticipants.add(participant);
- return this;
- }
-
- /**
- * Limits the query to only return threads that contain the given list of participants. If
- * this property was not set, participants will not be taken into account while querying
- * for threads.
- *
- * @param participants An iterable list of participants that must be included in all of the
- * returned threads.
- * @return The same instance of the builder to chain parameters.
- */
- @CheckResult
- public Builder setParticipants(@NonNull List<RcsParticipant> participants) {
- mParticipants.addAll(participants);
- return this;
- }
-
- /**
- * Desired number of threads to be returned from the query. Passing in 0 will return all
- * existing threads at once. The limit defaults to 100.
- *
- * @param limit The number to limit the query result to.
- * @return The same instance of the builder to chain parameters.
- * @throws InvalidParameterException If the given limit is negative.
- */
- @CheckResult
- public Builder setResultLimit(@IntRange(from = 0) int limit)
- throws InvalidParameterException {
- if (limit < 0) {
- throw new InvalidParameterException("The query limit must be non-negative");
- }
-
- mLimit = limit;
- return this;
- }
-
- /**
- * Sets the property where the results should be sorted against. Defaults to
- * {@link SortingProperty#SORT_BY_CREATION_ORDER}
- *
- * @param sortingProperty whether to sort in ascending order or not
- * @return The same instance of the builder to chain parameters.
- */
- @CheckResult
- public Builder setSortProperty(@SortingProperty int sortingProperty) {
- mSortingProperty = sortingProperty;
- return this;
- }
-
- /**
- * Sets whether the results should be sorted ascending or descending
- *
- * @param isAscending whether the results should be sorted ascending
- * @return The same instance of the builder to chain parameters.
- */
- @CheckResult
- public Builder setSortDirection(boolean isAscending) {
- mIsAscending = isAscending;
- return this;
- }
-
- /**
- * Builds the {@link RcsThreadQueryParams} to use in
- * {@link RcsMessageStore#getRcsThreads(RcsThreadQueryParams)}
- *
- * @return An instance of {@link RcsThreadQueryParams} to use with the thread query.
- */
- public RcsThreadQueryParams build() {
- return new RcsThreadQueryParams(mThreadType, mParticipants, mLimit,
- mSortingProperty, mIsAscending);
- }
- }
-
- /**
- * Parcelable boilerplate below.
- */
- private RcsThreadQueryParams(Parcel in) {
- mThreadType = in.readInt();
- mRcsParticipantIds = new ArrayList<>();
- in.readList(mRcsParticipantIds, Integer.class.getClassLoader());
- mLimit = in.readInt();
- mSortingProperty = in.readInt();
- mIsAscending = in.readByte() == 1;
- }
-
- public static final @android.annotation.NonNull Creator<RcsThreadQueryParams> CREATOR =
- new Creator<RcsThreadQueryParams>() {
- @Override
- public RcsThreadQueryParams createFromParcel(Parcel in) {
- return new RcsThreadQueryParams(in);
- }
-
- @Override
- public RcsThreadQueryParams[] newArray(int size) {
- return new RcsThreadQueryParams[size];
- }
- };
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public void writeToParcel(Parcel dest, int flags) {
- dest.writeInt(mThreadType);
- dest.writeList(mRcsParticipantIds);
- dest.writeInt(mLimit);
- dest.writeInt(mSortingProperty);
- dest.writeByte((byte) (mIsAscending ? 1 : 0));
- }
-}
diff --git a/telephony/java/android/telephony/ims/RcsThreadQueryResult.java b/telephony/java/android/telephony/ims/RcsThreadQueryResult.java
deleted file mode 100644
index 3de25de..0000000
--- a/telephony/java/android/telephony/ims/RcsThreadQueryResult.java
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.telephony.ims;
-
-import static android.provider.Telephony.RcsColumns.RcsUnifiedThreadColumns.THREAD_TYPE_1_TO_1;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-
-import java.util.List;
-import java.util.stream.Collectors;
-
-
-/**
- * The result of a {@link RcsMessageStore#getRcsThreads(RcsThreadQueryParams)}
- * call. This class allows getting the token for querying the next batch of threads in order to
- * prevent handling large amounts of data at once.
- *
- * @hide
- */
-public final class RcsThreadQueryResult {
- private final RcsControllerCall mRcsControllerCall;
- private final RcsThreadQueryResultParcelable mRcsThreadQueryResultParcelable;
-
- RcsThreadQueryResult(RcsControllerCall rcsControllerCall,
- RcsThreadQueryResultParcelable rcsThreadQueryResultParcelable) {
- mRcsControllerCall = rcsControllerCall;
- mRcsThreadQueryResultParcelable = rcsThreadQueryResultParcelable;
- }
-
- /**
- * Returns a token to call
- * {@link RcsMessageStore#getRcsThreads(RcsQueryContinuationToken)}
- * to get the next batch of {@link RcsThread}s.
- */
- @Nullable
- public RcsQueryContinuationToken getContinuationToken() {
- return mRcsThreadQueryResultParcelable.mContinuationToken;
- }
-
- /**
- * Returns all the RcsThreads in the current query result. Call {@link
- * RcsMessageStore#getRcsThreads(RcsQueryContinuationToken)} to get the next batch of
- * {@link RcsThread}s.
- */
- @NonNull
- public List<RcsThread> getThreads() {
- return mRcsThreadQueryResultParcelable.mRcsThreadIds.stream()
- .map(typeIdPair -> typeIdPair.getType() == THREAD_TYPE_1_TO_1
- ? new Rcs1To1Thread(mRcsControllerCall, typeIdPair.getId())
- : new RcsGroupThread(mRcsControllerCall, typeIdPair.getId()))
- .collect(Collectors.toList());
- }
-}
diff --git a/telephony/java/android/telephony/ims/RcsThreadQueryResultParcelable.aidl b/telephony/java/android/telephony/ims/RcsThreadQueryResultParcelable.aidl
deleted file mode 100644
index 05bd61d..0000000
--- a/telephony/java/android/telephony/ims/RcsThreadQueryResultParcelable.aidl
+++ /dev/null
@@ -1,20 +0,0 @@
-/*
- *
- * Copyright 2019, The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.telephony.ims;
-
-parcelable RcsThreadQueryResultParcelable;
diff --git a/telephony/java/android/telephony/ims/RcsThreadQueryResultParcelable.java b/telephony/java/android/telephony/ims/RcsThreadQueryResultParcelable.java
deleted file mode 100644
index 89dd1d4..0000000
--- a/telephony/java/android/telephony/ims/RcsThreadQueryResultParcelable.java
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.telephony.ims;
-
-import android.os.Parcel;
-import android.os.Parcelable;
-
-import com.android.ims.RcsTypeIdPair;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * @hide
- */
-public final class RcsThreadQueryResultParcelable implements Parcelable {
- final RcsQueryContinuationToken mContinuationToken;
- final List<RcsTypeIdPair> mRcsThreadIds;
-
- public RcsThreadQueryResultParcelable(
- RcsQueryContinuationToken continuationToken,
- List<RcsTypeIdPair> rcsThreadIds) {
- mContinuationToken = continuationToken;
- mRcsThreadIds = rcsThreadIds;
- }
-
- private RcsThreadQueryResultParcelable(Parcel in) {
- mContinuationToken = in.readParcelable(RcsQueryContinuationToken.class.getClassLoader());
- mRcsThreadIds = new ArrayList<>();
- in.readList(mRcsThreadIds, RcsTypeIdPair.class.getClassLoader());
- }
-
- public static final Parcelable.Creator<RcsThreadQueryResultParcelable> CREATOR =
- new Parcelable.Creator<RcsThreadQueryResultParcelable>() {
- @Override
- public RcsThreadQueryResultParcelable createFromParcel(Parcel in) {
- return new RcsThreadQueryResultParcelable(in);
- }
-
- @Override
- public RcsThreadQueryResultParcelable[] newArray(int size) {
- return new RcsThreadQueryResultParcelable[size];
- }
- };
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public void writeToParcel(Parcel dest, int flags) {
- dest.writeParcelable(mContinuationToken, flags);
- dest.writeList(mRcsThreadIds);
- }
-}
diff --git a/telephony/java/android/telephony/ims/aidl/IImsCallSessionListener.aidl b/telephony/java/android/telephony/ims/aidl/IImsCallSessionListener.aidl
index cc2ebb9..36d2067 100644
--- a/telephony/java/android/telephony/ims/aidl/IImsCallSessionListener.aidl
+++ b/telephony/java/android/telephony/ims/aidl/IImsCallSessionListener.aidl
@@ -148,6 +148,12 @@
void callSessionRttAudioIndicatorChanged(in ImsStreamMediaProfile profile);
/**
+ * Notifies the result of transfer request.
+ */
+ void callSessionTransferred();
+ void callSessionTransferFailed(in ImsReasonInfo reasonInfo);
+
+ /**
* Notifies of a change to the call quality.
* @param callQuality then updated call quality
*/
diff --git a/telephony/java/android/telephony/ims/aidl/IRcsMessage.aidl b/telephony/java/android/telephony/ims/aidl/IRcsMessage.aidl
deleted file mode 100644
index 0ae6303..0000000
--- a/telephony/java/android/telephony/ims/aidl/IRcsMessage.aidl
+++ /dev/null
@@ -1,266 +0,0 @@
-/*
- * 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 android.telephony.ims.aidl;
-
-import android.net.Uri;
-import android.telephony.ims.RcsEventQueryParams;
-import android.telephony.ims.RcsEventQueryResultDescriptor;
-import android.telephony.ims.RcsFileTransferCreationParams;
-import android.telephony.ims.RcsIncomingMessageCreationParams;
-import android.telephony.ims.RcsMessageSnippet;
-import android.telephony.ims.RcsMessageQueryParams;
-import android.telephony.ims.RcsMessageQueryResultParcelable;
-import android.telephony.ims.RcsOutgoingMessageCreationParams;
-import android.telephony.ims.RcsParticipantQueryParams;
-import android.telephony.ims.RcsParticipantQueryResultParcelable;
-import android.telephony.ims.RcsQueryContinuationToken;
-import android.telephony.ims.RcsThreadQueryParams;
-import android.telephony.ims.RcsThreadQueryResultParcelable;
-
-/**
- * RPC definition between RCS storage APIs and phone process.
- * {@hide}
- */
-interface IRcsMessage {
- /////////////////////////
- // RcsMessageStore APIs
- /////////////////////////
- RcsThreadQueryResultParcelable getRcsThreads(in RcsThreadQueryParams queryParams, String callingPackage);
-
- RcsThreadQueryResultParcelable getRcsThreadsWithToken(
- in RcsQueryContinuationToken continuationToken, String callingPackage);
-
- RcsParticipantQueryResultParcelable getParticipants(in RcsParticipantQueryParams queryParams, String callingPackage);
-
- RcsParticipantQueryResultParcelable getParticipantsWithToken(
- in RcsQueryContinuationToken continuationToken, String callingPackage);
-
- RcsMessageQueryResultParcelable getMessages(in RcsMessageQueryParams queryParams, String callingPackage);
-
- RcsMessageQueryResultParcelable getMessagesWithToken(
- in RcsQueryContinuationToken continuationToken, String callingPackage);
-
- RcsEventQueryResultDescriptor getEvents(in RcsEventQueryParams queryParams, String callingPackage);
-
- RcsEventQueryResultDescriptor getEventsWithToken(
- in RcsQueryContinuationToken continuationToken, String callingPackage);
-
- // returns true if the thread was successfully deleted
- boolean deleteThread(int threadId, int threadType, String callingPackage);
-
- // Creates an Rcs1To1Thread and returns its row ID
- int createRcs1To1Thread(int participantId, String callingPackage);
-
- // Creates an RcsGroupThread and returns its row ID
- int createGroupThread(in int[] participantIds, String groupName, in Uri groupIcon, String callingPackage);
-
- /////////////////////////
- // RcsThread APIs
- /////////////////////////
-
- // Creates a new RcsIncomingMessage on the given thread and returns its row ID
- int addIncomingMessage(int rcsThreadId,
- in RcsIncomingMessageCreationParams rcsIncomingMessageCreationParams, String callingPackage);
-
- // Creates a new RcsOutgoingMessage on the given thread and returns its row ID
- int addOutgoingMessage(int rcsThreadId,
- in RcsOutgoingMessageCreationParams rcsOutgoingMessageCreationParams, String callingPackage);
-
- // TODO: modify RcsProvider URI's to allow deleting a message without specifying its thread
- void deleteMessage(int rcsMessageId, boolean isIncoming, int rcsThreadId, boolean isGroup, String callingPackage);
-
- RcsMessageSnippet getMessageSnippet(int rcsThreadId, String callingPackage);
-
- /////////////////////////
- // Rcs1To1Thread APIs
- /////////////////////////
- void set1To1ThreadFallbackThreadId(int rcsThreadId, long fallbackId, String callingPackage);
-
- long get1To1ThreadFallbackThreadId(int rcsThreadId, String callingPackage);
-
- int get1To1ThreadOtherParticipantId(int rcsThreadId, String callingPackage);
-
- /////////////////////////
- // RcsGroupThread APIs
- /////////////////////////
- void setGroupThreadName(int rcsThreadId, String groupName, String callingPackage);
-
- String getGroupThreadName(int rcsThreadId, String callingPackage);
-
- void setGroupThreadIcon(int rcsThreadId, in Uri groupIcon, String callingPackage);
-
- Uri getGroupThreadIcon(int rcsThreadId, String callingPackage);
-
- void setGroupThreadOwner(int rcsThreadId, int participantId, String callingPackage);
-
- int getGroupThreadOwner(int rcsThreadId, String callingPackage);
-
- void setGroupThreadConferenceUri(int rcsThreadId, in Uri conferenceUri, String callingPackage);
-
- Uri getGroupThreadConferenceUri(int rcsThreadId, String callingPackage);
-
- void addParticipantToGroupThread(int rcsThreadId, int participantId, String callingPackage);
-
- void removeParticipantFromGroupThread(int rcsThreadId, int participantId, String callingPackage);
-
- /////////////////////////
- // RcsParticipant APIs
- /////////////////////////
-
- // Creates a new RcsParticipant and returns its rowId
- int createRcsParticipant(String canonicalAddress, String alias, String callingPackage);
-
- String getRcsParticipantCanonicalAddress(int participantId, String callingPackage);
-
- String getRcsParticipantAlias(int participantId, String callingPackage);
-
- void setRcsParticipantAlias(int id, String alias, String callingPackage);
-
- String getRcsParticipantContactId(int participantId, String callingPackage);
-
- void setRcsParticipantContactId(int participantId, String contactId, String callingPackage);
-
- /////////////////////////
- // RcsMessage APIs
- /////////////////////////
- void setMessageSubId(int messageId, boolean isIncoming, int subId, String callingPackage);
-
- int getMessageSubId(int messageId, boolean isIncoming, String callingPackage);
-
- void setMessageStatus(int messageId, boolean isIncoming, int status, String callingPackage);
-
- int getMessageStatus(int messageId, boolean isIncoming, String callingPackage);
-
- void setMessageOriginationTimestamp(int messageId, boolean isIncoming, long originationTimestamp, String callingPackage);
-
- long getMessageOriginationTimestamp(int messageId, boolean isIncoming, String callingPackage);
-
- void setGlobalMessageIdForMessage(int messageId, boolean isIncoming, String globalId, String callingPackage);
-
- String getGlobalMessageIdForMessage(int messageId, boolean isIncoming, String callingPackage);
-
- void setMessageArrivalTimestamp(int messageId, boolean isIncoming, long arrivalTimestamp, String callingPackage);
-
- long getMessageArrivalTimestamp(int messageId, boolean isIncoming, String callingPackage);
-
- void setMessageSeenTimestamp(int messageId, boolean isIncoming, long seenTimestamp, String callingPackage);
-
- long getMessageSeenTimestamp(int messageId, boolean isIncoming, String callingPackage);
-
- void setTextForMessage(int messageId, boolean isIncoming, String text, String callingPackage);
-
- String getTextForMessage(int messageId, boolean isIncoming, String callingPackage);
-
- void setLatitudeForMessage(int messageId, boolean isIncoming, double latitude, String callingPackage);
-
- double getLatitudeForMessage(int messageId, boolean isIncoming, String callingPackage);
-
- void setLongitudeForMessage(int messageId, boolean isIncoming, double longitude, String callingPackage);
-
- double getLongitudeForMessage(int messageId, boolean isIncoming, String callingPackage);
-
- // Returns the ID's of the file transfers attached to the given message
- int[] getFileTransfersAttachedToMessage(int messageId, boolean isIncoming, String callingPackage);
-
- int getSenderParticipant(int messageId, String callingPackage);
-
- /////////////////////////
- // RcsOutgoingMessageDelivery APIs
- /////////////////////////
-
- // Returns the participant ID's that this message is intended to be delivered to
- int[] getMessageRecipients(int messageId, String callingPackage);
-
- long getOutgoingDeliveryDeliveredTimestamp(int messageId, int participantId, String callingPackage);
-
- void setOutgoingDeliveryDeliveredTimestamp(int messageId, int participantId, long deliveredTimestamp, String callingPackage);
-
- long getOutgoingDeliverySeenTimestamp(int messageId, int participantId, String callingPackage);
-
- void setOutgoingDeliverySeenTimestamp(int messageId, int participantId, long seenTimestamp, String callingPackage);
-
- int getOutgoingDeliveryStatus(int messageId, int participantId, String callingPackage);
-
- void setOutgoingDeliveryStatus(int messageId, int participantId, int status, String callingPackage);
-
- /////////////////////////
- // RcsFileTransferPart APIs
- /////////////////////////
-
- // Performs the initial write to storage and returns the row ID.
- int storeFileTransfer(int messageId, boolean isIncoming,
- in RcsFileTransferCreationParams fileTransferCreationParams, String callingPackage);
-
- void deleteFileTransfer(int partId, String callingPackage);
-
- void setFileTransferSessionId(int partId, String sessionId, String callingPackage);
-
- String getFileTransferSessionId(int partId, String callingPackage);
-
- void setFileTransferContentUri(int partId, in Uri contentUri, String callingPackage);
-
- Uri getFileTransferContentUri(int partId, String callingPackage);
-
- void setFileTransferContentType(int partId, String contentType, String callingPackage);
-
- String getFileTransferContentType(int partId, String callingPackage);
-
- void setFileTransferFileSize(int partId, long fileSize, String callingPackage);
-
- long getFileTransferFileSize(int partId, String callingPackage);
-
- void setFileTransferTransferOffset(int partId, long transferOffset, String callingPackage);
-
- long getFileTransferTransferOffset(int partId, String callingPackage);
-
- void setFileTransferStatus(int partId, int transferStatus, String callingPackage);
-
- int getFileTransferStatus(int partId, String callingPackage);
-
- void setFileTransferWidth(int partId, int width, String callingPackage);
-
- int getFileTransferWidth(int partId, String callingPackage);
-
- void setFileTransferHeight(int partId, int height, String callingPackage);
-
- int getFileTransferHeight(int partId, String callingPackage);
-
- void setFileTransferLength(int partId, long length, String callingPackage);
-
- long getFileTransferLength(int partId, String callingPackage);
-
- void setFileTransferPreviewUri(int partId, in Uri uri, String callingPackage);
-
- Uri getFileTransferPreviewUri(int partId, String callingPackage);
-
- void setFileTransferPreviewType(int partId, String type, String callingPackage);
-
- String getFileTransferPreviewType(int partId, String callingPackage);
-
- /////////////////////////
- // RcsEvent APIs
- /////////////////////////
- int createGroupThreadNameChangedEvent(long timestamp, int threadId, int originationParticipantId, String newName, String callingPackage);
-
- int createGroupThreadIconChangedEvent(long timestamp, int threadId, int originationParticipantId, in Uri newIcon, String callingPackage);
-
- int createGroupThreadParticipantJoinedEvent(long timestamp, int threadId, int originationParticipantId, int participantId, String callingPackage);
-
- int createGroupThreadParticipantLeftEvent(long timestamp, int threadId, int originationParticipantId, int participantId, String callingPackage);
-
- int createParticipantAliasChangedEvent(long timestamp, int participantId, String newAlias, String callingPackage);
-}
\ No newline at end of file
diff --git a/telephony/java/android/telephony/ims/compat/stub/ImsCallSessionImplBase.java b/telephony/java/android/telephony/ims/compat/stub/ImsCallSessionImplBase.java
old mode 100644
new mode 100755
index 75bd6a7..06aa642
--- a/telephony/java/android/telephony/ims/compat/stub/ImsCallSessionImplBase.java
+++ b/telephony/java/android/telephony/ims/compat/stub/ImsCallSessionImplBase.java
@@ -16,6 +16,8 @@
package android.telephony.ims.compat.stub;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.compat.annotation.UnsupportedAppUsage;
import android.os.Message;
import android.os.RemoteException;
@@ -197,6 +199,29 @@
}
/**
+ * Transfer an established call to given number, disconnecting the ongoing call
+ * when the transfer is complete.
+ *
+ * @param number number to transfer the call
+ * @param isConfirmationRequired when {@code true}, then the {@link ImsCallSessionImplBase}
+ * should wait until the transfer has successfully completed before disconnecting the current
+ * {@link ImsCallSessionImplBase}. When {@code false}, the {@link ImsCallSessionImplBase}
+ * should signal the network to perform the transfer, but should immediately disconnect the
+ * call regardless of the outcome of the transfer.
+ */
+ @Override
+ public void transfer(@NonNull String number, boolean isConfirmationRequired) {
+ }
+
+ /**
+ * Transfer an established call to an existing ongoing session.
+ * When the transfer is complete, the current call gets disconnected locally.
+ */
+ @Override
+ public void consultativeTransfer(@NonNull IImsCallSession transferToSession) {
+ }
+
+ /**
* Rejects an incoming call or session update.
*
* @param reason reason code to reject an incoming call, defined in {@link ImsReasonInfo}.
@@ -610,6 +635,17 @@
}
@Override
+ public void callSessionTransferred() throws RemoteException {
+ mNewListener.callSessionTransferred();
+ }
+
+ @Override
+ public void callSessionTransferFailed(@Nullable ImsReasonInfo reasonInfo)
+ throws RemoteException {
+ mNewListener.callSessionTransferFailed(reasonInfo);
+ }
+
+ @Override
public void callQualityChanged(CallQuality callQuality) throws RemoteException {
mNewListener.callQualityChanged(callQuality);
}
diff --git a/telephony/java/android/telephony/ims/stub/ImsCallSessionImplBase.java b/telephony/java/android/telephony/ims/stub/ImsCallSessionImplBase.java
index e8f69ea..73ba0e3 100644
--- a/telephony/java/android/telephony/ims/stub/ImsCallSessionImplBase.java
+++ b/telephony/java/android/telephony/ims/stub/ImsCallSessionImplBase.java
@@ -16,6 +16,7 @@
package android.telephony.ims.stub;
+import android.annotation.NonNull;
import android.annotation.SystemApi;
import android.annotation.TestApi;
import android.os.Message;
@@ -183,6 +184,18 @@
}
@Override
+ public void transfer(@NonNull String number, boolean isConfirmationRequired) {
+ ImsCallSessionImplBase.this.transfer(number, isConfirmationRequired);
+ }
+
+ @Override
+ public void consultativeTransfer(@NonNull IImsCallSession transferToSession) {
+ ImsCallSessionImplBase otherSession = new ImsCallSessionImplBase();
+ otherSession.setServiceImpl(transferToSession);
+ ImsCallSessionImplBase.this.transfer(otherSession);
+ }
+
+ @Override
public void terminate(int reason) {
ImsCallSessionImplBase.this.terminate(reason);
}
@@ -423,6 +436,26 @@
}
/**
+ * Transfer an established call to given number
+ *
+ * @param number number to transfer the call
+ * @param isConfirmationRequired if {@code True}, indicates Assured transfer,
+ * if {@code False} it indicates Blind transfer.
+ * @hide
+ */
+ public void transfer(@NonNull String number, boolean isConfirmationRequired) {
+ }
+
+ /**
+ * Transfer an established call to another call session
+ *
+ * @param otherSession The other ImsCallSession to transfer the ongoing session to.
+ * @hide
+ */
+ public void transfer(@NonNull ImsCallSessionImplBase otherSession) {
+ }
+
+ /**
* Terminates a call.
*
* @param reason reason code to terminate a call, defined in {@link ImsReasonInfo}.
diff --git a/telephony/java/com/android/ims/internal/IImsCallSession.aidl b/telephony/java/com/android/ims/internal/IImsCallSession.aidl
index 15234e5..0466efc 100644
--- a/telephony/java/com/android/ims/internal/IImsCallSession.aidl
+++ b/telephony/java/com/android/ims/internal/IImsCallSession.aidl
@@ -18,7 +18,6 @@
import android.os.Message;
import android.telephony.ims.aidl.IImsCallSessionListener;
-
import android.telephony.ims.ImsCallProfile;
import android.telephony.ims.ImsStreamMediaProfile;
import com.android.ims.internal.IImsVideoCallProvider;
@@ -151,6 +150,22 @@
void reject(int reason);
/**
+ * Transfer an established call to given number
+ *
+ * @param number number to transfer the call
+ * @param isConfirmationRequired if {@code True}, indicates Assured transfer,
+ * if {@code False} it indicates Blind transfer.
+ */
+ void transfer(String number, boolean isConfirmationRequired);
+
+ /**
+ * Transfer an established call to another call session
+ *
+ * @param transferToSession The other ImsCallSession to transfer the ongoing session to.
+ */
+ void consultativeTransfer(in IImsCallSession transferToSession);
+
+ /**
* Terminates a call.
*
* @see Listener#callSessionTerminated
diff --git a/telephony/java/com/android/ims/internal/IImsCallSessionListener.aidl b/telephony/java/com/android/ims/internal/IImsCallSessionListener.aidl
index b33a9f1..1c62cc4 100644
--- a/telephony/java/com/android/ims/internal/IImsCallSessionListener.aidl
+++ b/telephony/java/com/android/ims/internal/IImsCallSessionListener.aidl
@@ -184,6 +184,12 @@
void callSessionRttAudioIndicatorChanged(in ImsStreamMediaProfile profile);
/**
+ * Notifies about the response for call transfer request.
+ */
+ void callSessionTransferred();
+
+ void callSessionTransferFailed(in ImsReasonInfo reasonInfo);
+ /**
* Notifies of a change to the call quality.
* @param callQuality then updated call quality
*/
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index 5c18cdd..0f2cb44 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -39,7 +39,6 @@
import android.telephony.ModemActivityInfo;
import android.telephony.NeighboringCellInfo;
import android.telephony.NetworkScanRequest;
-import android.telephony.PhoneCapability;
import android.telephony.PhoneNumberRange;
import android.telephony.RadioAccessFamily;
import android.telephony.ServiceState;
@@ -1622,16 +1621,6 @@
void carrierActionResetAll(int subId);
/**
- * Get aggregated video call data usage since boot.
- * Permissions android.Manifest.permission.READ_NETWORK_USAGE_HISTORY is required.
- *
- * @param perUidStats True if requesting data usage per uid, otherwise overall usage.
- * @return Snapshot of video call data usage
- * @hide
- */
- NetworkStats getVtDataUsage(int subId, boolean perUidStats);
-
- /**
* Gets the voice call forwarding info {@link CallForwardingInfo}, given the call forward
* reason.
*
@@ -1905,17 +1894,12 @@
/**
* Return the network selection mode on the subscription with id {@code subId}.
*/
- int getNetworkSelectionMode(int subId);
+ int getNetworkSelectionMode(int subId);
- /**
- * Return the PhoneCapability for the device.
- */
- PhoneCapability getPhoneCapability(int subId, String callingPackage, String callingFeatureId);
-
- /**
+ /**
* Return true if the device is in emergency sms mode, false otherwise.
*/
- boolean isInEmergencySmsMode();
+ boolean isInEmergencySmsMode();
/**
* Return the modem radio power state for slot index.
diff --git a/telephony/java/com/android/internal/telephony/cdma/sms/BearerData.java b/telephony/java/com/android/internal/telephony/cdma/sms/BearerData.java
index 39f1fc2..48cb1cd 100644
--- a/telephony/java/com/android/internal/telephony/cdma/sms/BearerData.java
+++ b/telephony/java/com/android/internal/telephony/cdma/sms/BearerData.java
@@ -248,10 +248,10 @@
public int hour;
public int monthDay;
- /** Month [0-11] */
- public int month;
+ /** Month in the range 1(Jan) - 12(Dec). */
+ public int monthOrdinal;
- /** Full year. For example, 1970. */
+ /** Full year in the range 1996 - 2095. */
public int year;
private ZoneId mZoneId;
@@ -269,7 +269,7 @@
ts.year = year >= 96 ? year + 1900 : year + 2000;
int month = IccUtils.cdmaBcdByteToInt(data[1]);
if (month < 1 || month > 12) return null;
- ts.month = month - 1;
+ ts.monthOrdinal = month;
int day = IccUtils.cdmaBcdByteToInt(data[2]);
if (day < 1 || day > 31) return null;
ts.monthDay = day;
@@ -292,7 +292,7 @@
int year = localDateTime.getYear();
if (year < 1996 || year > 2095) return null;
ts.year = year;
- ts.month = localDateTime.getMonthValue();
+ ts.monthOrdinal = localDateTime.getMonthValue();
ts.monthDay = localDateTime.getDayOfMonth();
ts.hour = localDateTime.getHour();
ts.minute = localDateTime.getMinute();
@@ -304,7 +304,7 @@
int year = this.year % 100; // 00 - 99
ByteArrayOutputStream outStream = new ByteArrayOutputStream(6);
outStream.write((((year / 10) & 0x0F) << 4) | ((year % 10) & 0x0F));
- outStream.write((((month / 10) << 4) & 0xF0) | ((month % 10) & 0x0F));
+ outStream.write((((monthOrdinal / 10) << 4) & 0xF0) | ((monthOrdinal % 10) & 0x0F));
outStream.write((((monthDay / 10) << 4) & 0xF0) | ((monthDay % 10) & 0x0F));
outStream.write((((hour / 10) << 4) & 0xF0) | ((hour % 10) & 0x0F));
outStream.write((((minute / 10) << 4) & 0xF0) | ((minute % 10) & 0x0F));
@@ -314,7 +314,7 @@
public long toMillis() {
LocalDateTime localDateTime =
- LocalDateTime.of(year, month + 1, monthDay, hour, minute, second);
+ LocalDateTime.of(year, monthOrdinal, monthDay, hour, minute, second);
Instant instant = localDateTime.toInstant(mZoneId.getRules().getOffset(localDateTime));
return instant.toEpochMilli();
}
@@ -325,7 +325,7 @@
StringBuilder builder = new StringBuilder();
builder.append("TimeStamp ");
builder.append("{ year=" + year);
- builder.append(", month=" + month);
+ builder.append(", month=" + monthOrdinal);
builder.append(", day=" + monthDay);
builder.append(", hour=" + hour);
builder.append(", minute=" + minute);
diff --git a/test-mock/Android.bp b/test-mock/Android.bp
index 616b6b0..248c117 100644
--- a/test-mock/Android.bp
+++ b/test-mock/Android.bp
@@ -28,7 +28,7 @@
":framework_native_aidl",
],
libs: [
- "framework-all",
+ "framework",
"app-compat-annotations",
"unsupportedappusage",
],
diff --git a/tests/FlickerTests/AndroidTest.xml b/tests/FlickerTests/AndroidTest.xml
index d1da47f..a331ec5 100644
--- a/tests/FlickerTests/AndroidTest.xml
+++ b/tests/FlickerTests/AndroidTest.xml
@@ -9,6 +9,14 @@
<option name="screen-always-on" value="on" />
<!-- prevents the phone from restarting -->
<option name="force-skip-system-props" value="true" />
+ <!-- set WM tracing verbose level to all -->
+ <option name="run-command" value="adb shell cmd window tracing level all" />
+ <!-- inform WM to log all transactions -->
+ <option name="run-command" value="adb shell cmd window tracing transaction" />
+ </target_preparer>
+ <target_preparer class="com.android.tradefed.targetprep.DeviceCleaner">
+ <!-- keeps the screen on during tests -->
+ <option name="cleanup-action" value="REBOOT" />
</target_preparer>
<target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
<option name="cleanup-apks" value="true"/>
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ChangeAppRotationTest.java b/tests/FlickerTests/src/com/android/server/wm/flicker/ChangeAppRotationTest.java
index 5a66e80..ad64840 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ChangeAppRotationTest.java
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ChangeAppRotationTest.java
@@ -144,6 +144,19 @@
);
}
+ @Test
+ public void checkVisibility_screenshotLayerBecomesInvisible() {
+ checkResults(result -> LayersTraceSubject.assertThat(result)
+ .showsLayer(mTestApp.getPackage())
+ .then()
+ .replaceVisibleLayer(mTestApp.getPackage(), "Screenshot")
+ .then()
+ .showsLayer(mTestApp.getPackage()).and().showsLayer("Screenshot")
+ .then()
+ .replaceVisibleLayer("Screenshot", mTestApp.getPackage())
+ .forAllEntries());
+ }
+
@FlakyTest(bugId = 140855415)
@Ignore("Waiting bug feedback")
@Test
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/CommonTransitions.java b/tests/FlickerTests/src/com/android/server/wm/flicker/CommonTransitions.java
index e033d0a..0201a95 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/CommonTransitions.java
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/CommonTransitions.java
@@ -20,7 +20,6 @@
import static android.view.Surface.rotationToString;
import static com.android.server.wm.flicker.helpers.AutomationUtils.clearRecents;
-import static com.android.server.wm.flicker.helpers.AutomationUtils.closePipWindow;
import static com.android.server.wm.flicker.helpers.AutomationUtils.exitSplitScreen;
import static com.android.server.wm.flicker.helpers.AutomationUtils.expandPipWindow;
import static com.android.server.wm.flicker.helpers.AutomationUtils.launchSplitScreen;
@@ -176,11 +175,15 @@
.repeat(ITERATIONS);
}
- static TransitionBuilder appToSplitScreen(IAppHelper testApp, UiDevice device) {
+ static TransitionBuilder appToSplitScreen(IAppHelper testApp, UiDevice device,
+ int beginRotation) {
+ final String testTag = "appToSplitScreen_" + testApp.getLauncherName() + "_"
+ + rotationToString(beginRotation);
return TransitionRunner.newBuilder()
- .withTag("appToSplitScreen_" + testApp.getLauncherName())
+ .withTag(testTag)
.recordAllRuns()
.runBeforeAll(AutomationUtils::wakeUpAndGoToHomeScreen)
+ .runBeforeAll(() -> setRotation(device, beginRotation))
.runBefore(testApp::open)
.runBefore(device::waitForIdle)
.runBefore(() -> sleep(500))
@@ -285,41 +288,52 @@
.repeat(ITERATIONS);
}
- static TransitionBuilder enterPipMode(PipAppHelper testApp, UiDevice device) {
+ static TransitionBuilder enterPipMode(PipAppHelper testApp, UiDevice device,
+ int beginRotation) {
return TransitionRunner.newBuilder()
- .withTag("enterPipMode_" + testApp.getLauncherName())
+ .withTag("enterPipMode_" + testApp.getLauncherName()
+ + rotationToString(beginRotation))
.runBeforeAll(AutomationUtils::wakeUpAndGoToHomeScreen)
.runBefore(device::pressHome)
+ .runBefore(() -> setRotation(device, beginRotation))
.runBefore(testApp::open)
.run(() -> testApp.clickEnterPipButton(device))
- .runAfter(() -> closePipWindow(device))
+ .runAfter(() -> testApp.closePipWindow(device))
.runAfterAll(testApp::exit)
.repeat(ITERATIONS);
}
- static TransitionBuilder exitPipModeToHome(PipAppHelper testApp, UiDevice device) {
+ static TransitionBuilder exitPipModeToHome(PipAppHelper testApp, UiDevice device,
+ int beginRotation) {
return TransitionRunner.newBuilder()
- .withTag("exitPipModeToHome_" + testApp.getLauncherName())
+ .withTag("exitPipModeToHome_" + testApp.getLauncherName()
+ + rotationToString(beginRotation))
+ .recordAllRuns()
.runBeforeAll(AutomationUtils::wakeUpAndGoToHomeScreen)
.runBefore(device::pressHome)
+ .runBefore(() -> setRotation(device, beginRotation))
.runBefore(testApp::open)
- .runBefore(() -> testApp.clickEnterPipButton(device))
- .run(() -> closePipWindow(device))
+ .run(() -> testApp.clickEnterPipButton(device))
+ .run(() -> testApp.closePipWindow(device))
.run(device::waitForIdle)
- .runAfterAll(testApp::exit)
+ .run(testApp::exit)
.repeat(ITERATIONS);
}
- static TransitionBuilder exitPipModeToApp(PipAppHelper testApp, UiDevice device) {
+ static TransitionBuilder exitPipModeToApp(PipAppHelper testApp, UiDevice device,
+ int beginRotation) {
return TransitionRunner.newBuilder()
- .withTag("exitPipModeToApp_" + testApp.getLauncherName())
+ .withTag("exitPipModeToApp_" + testApp.getLauncherName()
+ + rotationToString(beginRotation))
+ .recordAllRuns()
.runBeforeAll(AutomationUtils::wakeUpAndGoToHomeScreen)
- .runBefore(device::pressHome)
- .runBefore(testApp::open)
- .runBefore(() -> testApp.clickEnterPipButton(device))
+ .run(device::pressHome)
+ .run(() -> setRotation(device, beginRotation))
+ .run(testApp::open)
+ .run(() -> testApp.clickEnterPipButton(device))
.run(() -> expandPipWindow(device))
.run(device::waitForIdle)
- .runAfterAll(testApp::exit)
+ .run(testApp::exit)
.repeat(ITERATIONS);
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/DebugTest.java b/tests/FlickerTests/src/com/android/server/wm/flicker/DebugTest.java
index 8f0177c..666a0b9 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/DebugTest.java
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/DebugTest.java
@@ -94,7 +94,8 @@
*/
@Test
public void openAppToSplitScreen() {
- CommonTransitions.appToSplitScreen(testApp, uiDevice).includeJankyRuns().recordAllRuns()
+ CommonTransitions.appToSplitScreen(testApp, uiDevice,
+ Surface.ROTATION_0).includeJankyRuns().recordAllRuns()
.build().run();
}
@@ -116,7 +117,7 @@
ImeAppHelper bottomApp = new ImeAppHelper(InstrumentationRegistry.getInstrumentation());
CommonTransitions.resizeSplitScreen(testApp, bottomApp, uiDevice, Surface.ROTATION_0,
new Rational(1, 3), new Rational(2, 3))
- .includeJankyRuns().recordEachRun().build().run();
+ .includeJankyRuns().build().run();
}
// IME tests
@@ -128,7 +129,7 @@
public void editTextSetFocus() {
ImeAppHelper testApp = new ImeAppHelper(InstrumentationRegistry.getInstrumentation());
CommonTransitions.editTextSetFocus(testApp, uiDevice, Surface.ROTATION_0)
- .includeJankyRuns().recordEachRun()
+ .includeJankyRuns()
.build().run();
}
@@ -139,7 +140,7 @@
public void editTextLoseFocusToHome() {
ImeAppHelper testApp = new ImeAppHelper(InstrumentationRegistry.getInstrumentation());
CommonTransitions.editTextLoseFocusToHome(testApp, uiDevice, Surface.ROTATION_0)
- .includeJankyRuns().recordEachRun()
+ .includeJankyRuns()
.build().run();
}
@@ -150,7 +151,7 @@
public void editTextLoseFocusToApp() {
ImeAppHelper testApp = new ImeAppHelper(InstrumentationRegistry.getInstrumentation());
CommonTransitions.editTextLoseFocusToHome(testApp, uiDevice, Surface.ROTATION_0)
- .includeJankyRuns().recordEachRun()
+ .includeJankyRuns()
.build().run();
}
@@ -162,7 +163,7 @@
@Test
public void enterPipMode() {
PipAppHelper testApp = new PipAppHelper(InstrumentationRegistry.getInstrumentation());
- CommonTransitions.enterPipMode(testApp, uiDevice).includeJankyRuns().recordEachRun()
+ CommonTransitions.enterPipMode(testApp, uiDevice, Surface.ROTATION_0).includeJankyRuns()
.build().run();
}
@@ -172,7 +173,8 @@
@Test
public void exitPipModeToHome() {
PipAppHelper testApp = new PipAppHelper(InstrumentationRegistry.getInstrumentation());
- CommonTransitions.exitPipModeToHome(testApp, uiDevice).includeJankyRuns().recordEachRun()
+ CommonTransitions.exitPipModeToHome(testApp, uiDevice, Surface.ROTATION_0)
+ .includeJankyRuns()
.build().run();
}
@@ -182,7 +184,7 @@
@Test
public void exitPipModeToApp() {
PipAppHelper testApp = new PipAppHelper(InstrumentationRegistry.getInstrumentation());
- CommonTransitions.exitPipModeToApp(testApp, uiDevice).includeJankyRuns().recordEachRun()
+ CommonTransitions.exitPipModeToApp(testApp, uiDevice, Surface.ROTATION_0).includeJankyRuns()
.build().run();
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/FlickerTestBase.java b/tests/FlickerTests/src/com/android/server/wm/flicker/FlickerTestBase.java
index 883d59e..4578fa3 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/FlickerTestBase.java
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/FlickerTestBase.java
@@ -89,7 +89,7 @@
}
if (result.screenCaptureVideoExists()) {
Log.e(TAG, "Screen capture video saved to " + result
- .screenCaptureVideo.toString());
+ .screenCaptureVideoPath().toString());
}
}
});
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/OpenAppColdTest.java b/tests/FlickerTests/src/com/android/server/wm/flicker/OpenAppColdTest.java
index efdfaee..2981ff9 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/OpenAppColdTest.java
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/OpenAppColdTest.java
@@ -19,6 +19,8 @@
import static com.android.server.wm.flicker.CommonTransitions.openAppCold;
import static com.android.server.wm.flicker.WmTraceSubject.assertThat;
+import android.view.Surface;
+
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.FlakyTest;
import androidx.test.filters.LargeTest;
@@ -76,10 +78,20 @@
@Test
public void checkVisibility_wallpaperLayerBecomesInvisible() {
- checkResults(result -> LayersTraceSubject.assertThat(result)
- .showsLayer("Wallpaper")
- .then()
- .hidesLayer("Wallpaper")
- .forAllEntries());
+ if (mBeginRotation == Surface.ROTATION_0) {
+ checkResults(result -> LayersTraceSubject.assertThat(result)
+ .showsLayer("Wallpaper")
+ .then()
+ .replaceVisibleLayer("Wallpaper", mTestApp.getPackage())
+ .forAllEntries());
+ } else {
+ checkResults(result -> LayersTraceSubject.assertThat(result)
+ .showsLayer("Wallpaper")
+ .then()
+ .replaceVisibleLayer("Wallpaper", "Screenshot")
+ .then()
+ .showsLayer(mTestApp.getPackage())
+ .forAllEntries());
+ }
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/OpenAppToSplitScreenTest.java b/tests/FlickerTests/src/com/android/server/wm/flicker/OpenAppToSplitScreenTest.java
index f8b7938..ddead6d 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/OpenAppToSplitScreenTest.java
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/OpenAppToSplitScreenTest.java
@@ -17,35 +17,38 @@
package com.android.server.wm.flicker;
import static com.android.server.wm.flicker.CommonTransitions.appToSplitScreen;
-import static com.android.server.wm.flicker.WindowUtils.getDisplayBounds;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.LargeTest;
-import androidx.test.runner.AndroidJUnit4;
import org.junit.Before;
import org.junit.FixMethodOrder;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.MethodSorters;
+import org.junit.runners.Parameterized;
/**
* Test open app to split screen.
* To run this test: {@code atest FlickerTests:OpenAppToSplitScreenTest}
*/
@LargeTest
-@RunWith(AndroidJUnit4.class)
+@RunWith(Parameterized.class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-public class OpenAppToSplitScreenTest extends FlickerTestBase {
+public class OpenAppToSplitScreenTest extends NonRotationTestBase {
- public OpenAppToSplitScreenTest() {
+ public OpenAppToSplitScreenTest(String beginRotationName, int beginRotation) {
+ super(beginRotationName, beginRotation);
+
this.mTestApp = new StandardAppHelper(InstrumentationRegistry.getInstrumentation(),
"com.android.server.wm.flicker.testapp", "SimpleApp");
}
@Before
public void runTransition() {
- super.runTransition(appToSplitScreen(mTestApp, mUiDevice).includeJankyRuns().build());
+ super.runTransition(appToSplitScreen(mTestApp, mUiDevice, mBeginRotation)
+ .includeJankyRuns()
+ .build());
}
@Test
@@ -70,25 +73,6 @@
}
@Test
- public void checkCoveredRegion_noUncoveredRegions() {
- checkResults(result ->
- LayersTraceSubject.assertThat(result)
- .coversRegion(getDisplayBounds()).forAllEntries());
- }
-
- @Test
- public void checkVisibility_navBarLayerIsAlwaysVisible() {
- checkResults(result -> LayersTraceSubject.assertThat(result)
- .showsLayer(NAVIGATION_BAR_WINDOW_TITLE).forAllEntries());
- }
-
- @Test
- public void checkVisibility_statusBarLayerIsAlwaysVisible() {
- checkResults(result -> LayersTraceSubject.assertThat(result)
- .showsLayer(STATUS_BAR_WINDOW_TITLE).forAllEntries());
- }
-
- @Test
public void checkVisibility_dividerLayerBecomesVisible() {
checkResults(result -> LayersTraceSubject.assertThat(result)
.hidesLayer(DOCKED_STACK_DIVIDER)
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/OpenAppWarmTest.java b/tests/FlickerTests/src/com/android/server/wm/flicker/OpenAppWarmTest.java
index 7ce6315..bb684d1 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/OpenAppWarmTest.java
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/OpenAppWarmTest.java
@@ -19,6 +19,8 @@
import static com.android.server.wm.flicker.CommonTransitions.openAppWarm;
import static com.android.server.wm.flicker.WmTraceSubject.assertThat;
+import android.view.Surface;
+
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.FlakyTest;
import androidx.test.filters.LargeTest;
@@ -76,10 +78,20 @@
@Test
public void checkVisibility_wallpaperLayerBecomesInvisible() {
- checkResults(result -> LayersTraceSubject.assertThat(result)
- .showsLayer("Wallpaper")
- .then()
- .hidesLayer("Wallpaper")
- .forAllEntries());
+ if (mBeginRotation == Surface.ROTATION_0) {
+ checkResults(result -> LayersTraceSubject.assertThat(result)
+ .showsLayer("Wallpaper")
+ .then()
+ .replaceVisibleLayer("Wallpaper", mTestApp.getPackage())
+ .forAllEntries());
+ } else {
+ checkResults(result -> LayersTraceSubject.assertThat(result)
+ .showsLayer("Wallpaper")
+ .then()
+ .replaceVisibleLayer("Wallpaper", "Screenshot")
+ .then()
+ .showsLayer(mTestApp.getPackage())
+ .forAllEntries());
+ }
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/PipToAppTest.java b/tests/FlickerTests/src/com/android/server/wm/flicker/PipToAppTest.java
new file mode 100644
index 0000000..85706bd
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/PipToAppTest.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2019 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.wm.flicker;
+
+import static com.android.server.wm.flicker.CommonTransitions.exitPipModeToApp;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.LargeTest;
+
+import com.android.server.wm.flicker.helpers.PipAppHelper;
+
+import org.junit.Before;
+import org.junit.FixMethodOrder;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.MethodSorters;
+import org.junit.runners.Parameterized;
+
+/**
+ * Test Pip launch.
+ * To run this test: {@code atest FlickerTests:PipToAppTest}
+ */
+@LargeTest
+@RunWith(Parameterized.class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+public class PipToAppTest extends NonRotationTestBase {
+
+ static final String sPipWindowTitle = "PipMenuActivity";
+
+ public PipToAppTest(String beginRotationName, int beginRotation) {
+ super(beginRotationName, beginRotation);
+
+ this.mTestApp = new PipAppHelper(InstrumentationRegistry.getInstrumentation());
+ }
+
+ @Before
+ public void runTransition() {
+ run(exitPipModeToApp((PipAppHelper) mTestApp, mUiDevice, mBeginRotation)
+ .includeJankyRuns().build());
+ }
+
+ @Test
+ public void checkVisibility_pipWindowBecomesVisible() {
+ checkResults(result -> WmTraceSubject.assertThat(result)
+ .skipUntilFirstAssertion()
+ .showsAppWindowOnTop(sPipWindowTitle)
+ .then()
+ .hidesAppWindow(sPipWindowTitle)
+ .forAllEntries());
+ }
+
+ @Test
+ public void checkVisibility_pipLayerBecomesVisible() {
+ checkResults(result -> LayersTraceSubject.assertThat(result)
+ .skipUntilFirstAssertion()
+ .showsLayer(sPipWindowTitle)
+ .then()
+ .hidesLayer(sPipWindowTitle)
+ .forAllEntries());
+ }
+
+ @Test
+ public void checkVisibility_backgroundWindowVisibleBehindPipLayer() {
+ checkResults(result -> WmTraceSubject.assertThat(result)
+ .skipUntilFirstAssertion()
+ .showsAppWindowOnTop(sPipWindowTitle)
+ .then()
+ .showsBelowAppWindow("Wallpaper")
+ .then()
+ .showsAppWindowOnTop(mTestApp.getPackage())
+ .then()
+ .hidesAppWindowOnTop(mTestApp.getPackage())
+ .forAllEntries());
+ }
+}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/PipToHomeTest.java b/tests/FlickerTests/src/com/android/server/wm/flicker/PipToHomeTest.java
new file mode 100644
index 0000000..ef856dc
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/PipToHomeTest.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2019 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.wm.flicker;
+
+import static com.android.server.wm.flicker.CommonTransitions.exitPipModeToHome;
+
+import android.view.Surface;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.LargeTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.server.wm.flicker.helpers.PipAppHelper;
+
+import org.junit.Before;
+import org.junit.FixMethodOrder;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.MethodSorters;
+
+/**
+ * Test Pip launch.
+ * To run this test: {@code atest FlickerTests:PipToHomeTest}
+ */
+@LargeTest
+@RunWith(AndroidJUnit4.class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+public class PipToHomeTest extends FlickerTestBase {
+
+ static final String sPipWindowTitle = "PipActivity";
+
+ // public PipToHomeTest(String beginRotationName, int beginRotation) {
+ public PipToHomeTest() {
+ // super(beginRotationName, beginRotation);
+
+ this.mTestApp = new PipAppHelper(InstrumentationRegistry.getInstrumentation());
+ }
+
+ @Before
+ public void runTransition() {
+ // run(exitPipModeToHome((PipAppHelper) mTestApp, mUiDevice, mBeginRotation)
+ run(exitPipModeToHome((PipAppHelper) mTestApp, mUiDevice, Surface.ROTATION_0)
+ .includeJankyRuns().build());
+ }
+
+ @Ignore
+ @Test
+ public void checkVisibility_pipWindowBecomesVisible() {
+ checkResults(result -> WmTraceSubject.assertThat(result)
+ .skipUntilFirstAssertion()
+ .showsAppWindowOnTop(sPipWindowTitle)
+ .then()
+ .hidesAppWindow(sPipWindowTitle)
+ .forAllEntries());
+ }
+
+ @Test
+ public void checkVisibility_pipLayerBecomesVisible() {
+ checkResults(result -> LayersTraceSubject.assertThat(result)
+ .skipUntilFirstAssertion()
+ .showsLayer(sPipWindowTitle)
+ .then()
+ .hidesLayer(sPipWindowTitle)
+ .forAllEntries());
+ }
+
+ @Ignore
+ @Test
+ public void checkVisibility_backgroundWindowVisibleBehindPipLayer() {
+ checkResults(result -> WmTraceSubject.assertThat(result)
+ .showsAppWindowOnTop(sPipWindowTitle)
+ .then()
+ .showsBelowAppWindow("Wallpaper")
+ .then()
+ .showsAppWindowOnTop("Wallpaper")
+ .forAllEntries());
+ }
+}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ResizeSplitScreenTest.java b/tests/FlickerTests/src/com/android/server/wm/flicker/ResizeSplitScreenTest.java
index 29b6240..e36701b 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ResizeSplitScreenTest.java
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ResizeSplitScreenTest.java
@@ -95,7 +95,7 @@
Rect displayBounds = getDisplayBounds();
checkResults(result -> {
LayersTrace entries = LayersTrace.parseFrom(result.getLayersTrace(),
- result.getLayersTracePath());
+ result.getLayersTracePath(), result.getLayersTraceChecksum());
assertThat(entries.getEntries()).isNotEmpty();
Rect startingDividerBounds = entries.getEntries().get(0).getVisibleBounds
@@ -124,7 +124,7 @@
Rect displayBounds = getDisplayBounds();
checkResults(result -> {
LayersTrace entries = LayersTrace.parseFrom(result.getLayersTrace(),
- result.getLayersTracePath());
+ result.getLayersTracePath(), result.getLayersTraceChecksum());
assertThat(entries.getEntries()).isNotEmpty();
Rect endingDividerBounds = entries.getEntries().get(
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/PipAppHelper.java b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/PipAppHelper.java
index d00e11b..d5f9a20 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/PipAppHelper.java
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/PipAppHelper.java
@@ -40,4 +40,8 @@
}
}
+ public void closePipWindow(UiDevice device) {
+ AutomationUtils.closePipWindow(device);
+ }
+
}
diff --git a/tests/RcsTests/Android.bp b/tests/RcsTests/Android.bp
deleted file mode 100644
index 8ee4960..0000000
--- a/tests/RcsTests/Android.bp
+++ /dev/null
@@ -1,17 +0,0 @@
-android_test {
- name: "RcsTests",
- // Only compile source java files in this apk.
- srcs: ["src/**/*.java"],
- platform_apis: true,
- certificate: "platform",
- libs: [
- "android.test.runner",
- "android.test.base",
- ],
- static_libs: [
- "junit",
- "androidx.test.rules",
- "mockito-target-minus-junit4",
- "truth-prebuilt",
- ],
-}
diff --git a/tests/RcsTests/AndroidManifest.xml b/tests/RcsTests/AndroidManifest.xml
deleted file mode 100644
index b1706a0..0000000
--- a/tests/RcsTests/AndroidManifest.xml
+++ /dev/null
@@ -1,11 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.android.tests.rcs">
- <application android:label="RCS Test">
- <uses-library android:name="android.test.runner" />
- </application>
-
- <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
- android:targetPackage="com.android.tests.rcs"/>
-</manifest>
diff --git a/tests/RcsTests/src/com/android/tests/ims/RcsGroupThreadIconChangedEventTest.java b/tests/RcsTests/src/com/android/tests/ims/RcsGroupThreadIconChangedEventTest.java
deleted file mode 100644
index 6c311f9..0000000
--- a/tests/RcsTests/src/com/android/tests/ims/RcsGroupThreadIconChangedEventTest.java
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * Copyright (C) 2019 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.tests.ims;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import android.net.Uri;
-import android.os.Parcel;
-import android.telephony.ims.RcsGroupThreadIconChangedEvent;
-import android.telephony.ims.RcsGroupThreadIconChangedEventDescriptor;
-
-import androidx.test.runner.AndroidJUnit4;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-@RunWith(AndroidJUnit4.class)
-public class RcsGroupThreadIconChangedEventTest {
-
- @Test
- public void testCanUnparcel() {
- int rcsGroupThreadId = 1;
- int rcsParticipantId = 2;
- Uri newIconUri = Uri.parse("content://new_icon");
-
- RcsGroupThreadIconChangedEventDescriptor iconChangedEventDescriptor =
- new RcsGroupThreadIconChangedEventDescriptor(1234567890, rcsGroupThreadId,
- rcsParticipantId, newIconUri);
-
- Parcel parcel = Parcel.obtain();
- iconChangedEventDescriptor.writeToParcel(
- parcel, iconChangedEventDescriptor.describeContents());
-
- parcel.setDataPosition(0);
-
- iconChangedEventDescriptor =
- RcsGroupThreadIconChangedEventDescriptor.CREATOR.createFromParcel(parcel);
-
- RcsGroupThreadIconChangedEvent iconChangedEvent =
- iconChangedEventDescriptor.createRcsEvent(null);
-
- assertThat(iconChangedEvent.getNewIcon()).isEqualTo(newIconUri);
- assertThat(iconChangedEvent.getRcsGroupThread().getThreadId()).isEqualTo(1);
- assertThat(iconChangedEvent.getOriginatingParticipant().getId()).isEqualTo(2);
- assertThat(iconChangedEvent.getTimestamp()).isEqualTo(1234567890);
- }
-}
diff --git a/tests/RcsTests/src/com/android/tests/ims/RcsGroupThreadNameChangedEventTest.java b/tests/RcsTests/src/com/android/tests/ims/RcsGroupThreadNameChangedEventTest.java
deleted file mode 100644
index a60dabb..0000000
--- a/tests/RcsTests/src/com/android/tests/ims/RcsGroupThreadNameChangedEventTest.java
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * Copyright (C) 2019 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.tests.ims;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import android.os.Parcel;
-import android.telephony.ims.RcsGroupThreadNameChangedEvent;
-import android.telephony.ims.RcsGroupThreadNameChangedEventDescriptor;
-
-import androidx.test.runner.AndroidJUnit4;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-@RunWith(AndroidJUnit4.class)
-public class RcsGroupThreadNameChangedEventTest {
- @Test
- public void testCanUnparcel() {
- String newName = "new name";
-
- int rcsGroupThreadId = 1;
- int rcsParticipantId = 2;
-
- RcsGroupThreadNameChangedEventDescriptor nameChangedEventDescriptor =
- new RcsGroupThreadNameChangedEventDescriptor(
- 1234567890, rcsGroupThreadId, rcsParticipantId, newName);
-
- Parcel parcel = Parcel.obtain();
- nameChangedEventDescriptor.writeToParcel(
- parcel, nameChangedEventDescriptor.describeContents());
-
- parcel.setDataPosition(0);
-
- nameChangedEventDescriptor = RcsGroupThreadNameChangedEventDescriptor.CREATOR
- .createFromParcel(parcel);
-
- RcsGroupThreadNameChangedEvent nameChangedEvent =
- nameChangedEventDescriptor.createRcsEvent(null);
-
- assertThat(nameChangedEvent.getNewName()).isEqualTo(newName);
- assertThat(nameChangedEvent.getRcsGroupThread().getThreadId()).isEqualTo(1);
- assertThat(nameChangedEvent.getOriginatingParticipant().getId()).isEqualTo(2);
- assertThat(nameChangedEvent.getTimestamp()).isEqualTo(1234567890);
- }
-}
diff --git a/tests/RcsTests/src/com/android/tests/ims/RcsGroupThreadParticipantJoinedEventTest.java b/tests/RcsTests/src/com/android/tests/ims/RcsGroupThreadParticipantJoinedEventTest.java
deleted file mode 100644
index 7b02cb1..0000000
--- a/tests/RcsTests/src/com/android/tests/ims/RcsGroupThreadParticipantJoinedEventTest.java
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * Copyright (C) 2019 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.tests.ims;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import android.os.Parcel;
-import android.telephony.ims.RcsGroupThreadParticipantJoinedEvent;
-import android.telephony.ims.RcsGroupThreadParticipantJoinedEventDescriptor;
-
-import androidx.test.runner.AndroidJUnit4;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-@RunWith(AndroidJUnit4.class)
-public class RcsGroupThreadParticipantJoinedEventTest {
-
- @Test
- public void testCanUnparcel() {
- int rcsGroupThreadId = 1;
- int rcsParticipantId = 2;
-
- RcsGroupThreadParticipantJoinedEventDescriptor participantJoinedEventDescriptor =
- new RcsGroupThreadParticipantJoinedEventDescriptor(
- 1234567890, rcsGroupThreadId, rcsParticipantId, rcsParticipantId);
-
- Parcel parcel = Parcel.obtain();
- participantJoinedEventDescriptor.writeToParcel(
- parcel, participantJoinedEventDescriptor.describeContents());
-
- parcel.setDataPosition(0);
-
- participantJoinedEventDescriptor = RcsGroupThreadParticipantJoinedEventDescriptor.CREATOR
- .createFromParcel(parcel);
-
- RcsGroupThreadParticipantJoinedEvent participantJoinedEvent =
- participantJoinedEventDescriptor.createRcsEvent(null);
-
- assertThat(participantJoinedEvent.getJoinedParticipant().getId()).isEqualTo(2);
- assertThat(participantJoinedEvent.getRcsGroupThread().getThreadId()).isEqualTo(1);
- assertThat(participantJoinedEvent.getOriginatingParticipant().getId()).isEqualTo(2);
- assertThat(participantJoinedEvent.getTimestamp()).isEqualTo(1234567890);
- }
-}
diff --git a/tests/RcsTests/src/com/android/tests/ims/RcsGroupThreadParticipantLeftEventTest.java b/tests/RcsTests/src/com/android/tests/ims/RcsGroupThreadParticipantLeftEventTest.java
deleted file mode 100644
index 51466bd..0000000
--- a/tests/RcsTests/src/com/android/tests/ims/RcsGroupThreadParticipantLeftEventTest.java
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * Copyright (C) 2019 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.tests.ims;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import android.os.Parcel;
-import android.telephony.ims.RcsGroupThreadParticipantLeftEvent;
-import android.telephony.ims.RcsGroupThreadParticipantLeftEventDescriptor;
-
-import androidx.test.runner.AndroidJUnit4;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-@RunWith(AndroidJUnit4.class)
-public class RcsGroupThreadParticipantLeftEventTest {
- @Test
- public void testCanUnparcel() {
- int rcsGroupThreadId = 1;
- int rcsParticipantId = 2;
-
- RcsGroupThreadParticipantLeftEventDescriptor participantLeftEventDescriptor =
- new RcsGroupThreadParticipantLeftEventDescriptor(
- 1234567890, rcsGroupThreadId, rcsParticipantId, rcsParticipantId);
-
- Parcel parcel = Parcel.obtain();
- participantLeftEventDescriptor.writeToParcel(
- parcel, participantLeftEventDescriptor.describeContents());
-
- parcel.setDataPosition(0);
-
- // create from parcel
- parcel.setDataPosition(0);
- participantLeftEventDescriptor = RcsGroupThreadParticipantLeftEventDescriptor.CREATOR
- .createFromParcel(parcel);
-
- RcsGroupThreadParticipantLeftEvent participantLeftEvent =
- participantLeftEventDescriptor.createRcsEvent(null);
-
- assertThat(participantLeftEvent.getRcsGroupThread().getThreadId()).isEqualTo(1);
- assertThat(participantLeftEvent.getLeavingParticipant().getId()).isEqualTo(2);
- assertThat(participantLeftEvent.getTimestamp()).isEqualTo(1234567890);
- }
-}
diff --git a/tests/RcsTests/src/com/android/tests/ims/RcsParticipantAliasChangedEventTest.java b/tests/RcsTests/src/com/android/tests/ims/RcsParticipantAliasChangedEventTest.java
deleted file mode 100644
index 56830df..0000000
--- a/tests/RcsTests/src/com/android/tests/ims/RcsParticipantAliasChangedEventTest.java
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * Copyright (C) 2019 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.tests.ims;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import android.os.Parcel;
-import android.telephony.ims.RcsParticipantAliasChangedEvent;
-import android.telephony.ims.RcsParticipantAliasChangedEventDescriptor;
-
-import androidx.test.runner.AndroidJUnit4;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-@RunWith(AndroidJUnit4.class)
-public class RcsParticipantAliasChangedEventTest {
- private static final String OLD_ALIAS = "old alias";
- private static final String NEW_ALIAS = "new alias";
- private int mParticipantId = 3;
-
- @Test
- public void testCanUnparcel() {
- RcsParticipantAliasChangedEventDescriptor aliasChangedEventDescriptor =
- new RcsParticipantAliasChangedEventDescriptor(
- 1234567890, mParticipantId, NEW_ALIAS);
-
- Parcel parcel = Parcel.obtain();
- aliasChangedEventDescriptor.writeToParcel(
- parcel, aliasChangedEventDescriptor.describeContents());
-
- parcel.setDataPosition(0);
-
- aliasChangedEventDescriptor = RcsParticipantAliasChangedEventDescriptor.CREATOR
- .createFromParcel(parcel);
-
- RcsParticipantAliasChangedEvent aliasChangedEvent =
- aliasChangedEventDescriptor.createRcsEvent(null);
-
- assertThat(aliasChangedEvent.getParticipant().getId()).isEqualTo(mParticipantId);
- assertThat(aliasChangedEvent.getNewAlias()).isEqualTo(NEW_ALIAS);
- assertThat(aliasChangedEvent.getTimestamp()).isEqualTo(1234567890);
- }
-}
diff --git a/tests/RcsTests/src/com/android/tests/ims/RcsParticipantQueryParamsTest.java b/tests/RcsTests/src/com/android/tests/ims/RcsParticipantQueryParamsTest.java
deleted file mode 100644
index 2d95513b..0000000
--- a/tests/RcsTests/src/com/android/tests/ims/RcsParticipantQueryParamsTest.java
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * 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.tests.ims;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import android.os.Parcel;
-import android.telephony.ims.RcsParticipantQueryParams;
-
-import androidx.test.runner.AndroidJUnit4;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-@RunWith(AndroidJUnit4.class)
-public class RcsParticipantQueryParamsTest {
-
- @Test
- public void testCanUnparcel() {
- RcsParticipantQueryParams rcsParticipantQueryParams =
- new RcsParticipantQueryParams.Builder()
- .setAliasLike("%alias_")
- .setCanonicalAddressLike("_canonical%")
- .setSortProperty(RcsParticipantQueryParams.SORT_BY_CANONICAL_ADDRESS)
- .setSortDirection(true)
- .setResultLimit(432)
- .build();
-
-
- Parcel parcel = Parcel.obtain();
- rcsParticipantQueryParams.writeToParcel(parcel,
- rcsParticipantQueryParams.describeContents());
-
- parcel.setDataPosition(0);
- rcsParticipantQueryParams = RcsParticipantQueryParams.CREATOR.createFromParcel(
- parcel);
-
- assertThat(rcsParticipantQueryParams.getAliasLike()).isEqualTo("%alias_");
- assertThat(rcsParticipantQueryParams.getCanonicalAddressLike()).contains("_canonical%");
- assertThat(rcsParticipantQueryParams.getLimit()).isEqualTo(432);
- assertThat(rcsParticipantQueryParams.getSortingProperty()).isEqualTo(
- RcsParticipantQueryParams.SORT_BY_CANONICAL_ADDRESS);
- assertThat(rcsParticipantQueryParams.getSortDirection()).isTrue();
- }
-}
diff --git a/tests/RcsTests/src/com/android/tests/ims/RcsThreadQueryParamsTest.java b/tests/RcsTests/src/com/android/tests/ims/RcsThreadQueryParamsTest.java
deleted file mode 100644
index 7a3e384..0000000
--- a/tests/RcsTests/src/com/android/tests/ims/RcsThreadQueryParamsTest.java
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * 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.tests.ims;
-
-import static android.telephony.ims.RcsThreadQueryParams.SORT_BY_TIMESTAMP;
-import static android.telephony.ims.RcsThreadQueryParams.THREAD_TYPE_GROUP;
-
-import static com.google.common.truth.Truth.assertThat;
-
-import android.os.Parcel;
-import android.telephony.ims.RcsParticipant;
-import android.telephony.ims.RcsThreadQueryParams;
-
-import androidx.test.runner.AndroidJUnit4;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-@RunWith(AndroidJUnit4.class)
-public class RcsThreadQueryParamsTest {
-
- @Test
- public void testCanUnparcel() {
- RcsParticipant rcsParticipant = new RcsParticipant(null, 1);
- RcsThreadQueryParams rcsThreadQueryParams = new RcsThreadQueryParams.Builder()
- .setThreadType(THREAD_TYPE_GROUP)
- .setParticipant(rcsParticipant)
- .setResultLimit(50)
- .setSortProperty(SORT_BY_TIMESTAMP)
- .setSortDirection(true)
- .build();
-
- Parcel parcel = Parcel.obtain();
- rcsThreadQueryParams.writeToParcel(parcel, rcsThreadQueryParams.describeContents());
-
- parcel.setDataPosition(0);
- rcsThreadQueryParams = RcsThreadQueryParams.CREATOR.createFromParcel(parcel);
-
- assertThat(rcsThreadQueryParams.getThreadType()).isEqualTo(THREAD_TYPE_GROUP);
- assertThat(rcsThreadQueryParams.getRcsParticipantsIds())
- .contains(rcsParticipant.getId());
- assertThat(rcsThreadQueryParams.getLimit()).isEqualTo(50);
- assertThat(rcsThreadQueryParams.getSortingProperty()).isEqualTo(SORT_BY_TIMESTAMP);
- assertThat(rcsThreadQueryParams.getSortDirection()).isTrue();
- }
-}
diff --git a/tests/net/AndroidManifest.xml b/tests/net/AndroidManifest.xml
index 638b6d1..480b12b 100644
--- a/tests/net/AndroidManifest.xml
+++ b/tests/net/AndroidManifest.xml
@@ -50,6 +50,7 @@
<application>
<uses-library android:name="android.test.runner" />
+ <uses-library android:name="android.net.ipsec.ike" />
</application>
<instrumentation
diff --git a/tests/net/common/java/android/net/LinkPropertiesTest.java b/tests/net/common/java/android/net/LinkPropertiesTest.java
index f25fd4d..48b65e5 100644
--- a/tests/net/common/java/android/net/LinkPropertiesTest.java
+++ b/tests/net/common/java/android/net/LinkPropertiesTest.java
@@ -315,7 +315,7 @@
source.addDnsServer(DNS1);
source.addDnsServer(DNS2);
// set 2 gateways
- source.addRoute(new RouteInfo(GATEWAY1));
+ source.addRoute(new RouteInfo(LINKADDRV4, GATEWAY1));
source.addRoute(new RouteInfo(GATEWAY2));
source.setMtu(MTU);
@@ -327,7 +327,7 @@
target.addDnsServer(DNS2);
target.addDnsServer(DNS1);
target.addRoute(new RouteInfo(GATEWAY2));
- target.addRoute(new RouteInfo(GATEWAY1));
+ target.addRoute(new RouteInfo(LINKADDRV4, GATEWAY1));
target.setMtu(MTU);
assertLinkPropertiesEqual(source, target);
@@ -364,12 +364,13 @@
@Test
public void testRouteInterfaces() {
- LinkAddress prefix = new LinkAddress(address("2001:db8::"), 32);
+ LinkAddress prefix1 = new LinkAddress(address("2001:db8:1::"), 48);
+ LinkAddress prefix2 = new LinkAddress(address("2001:db8:2::"), 48);
InetAddress address = ADDRV6;
// Add a route with no interface to a LinkProperties with no interface. No errors.
LinkProperties lp = new LinkProperties();
- RouteInfo r = new RouteInfo(prefix, address, null);
+ RouteInfo r = new RouteInfo(prefix1, address, null);
assertTrue(lp.addRoute(r));
assertEquals(1, lp.getRoutes().size());
assertAllRoutesHaveInterface(null, lp);
@@ -379,7 +380,7 @@
assertEquals(1, lp.getRoutes().size());
// Add a route with an interface. Expect an exception.
- r = new RouteInfo(prefix, address, "wlan0");
+ r = new RouteInfo(prefix2, address, "wlan0");
try {
lp.addRoute(r);
fail("Adding wlan0 route to LP with no interface, expect exception");
@@ -398,7 +399,7 @@
} catch (IllegalArgumentException expected) {}
// If the interface name matches, the route is added.
- r = new RouteInfo(prefix, null, "wlan0");
+ r = new RouteInfo(prefix2, null, "wlan0");
lp.setInterfaceName("wlan0");
lp.addRoute(r);
assertEquals(2, lp.getRoutes().size());
@@ -423,10 +424,12 @@
assertEquals(3, lp.compareAllRoutes(lp2).added.size());
assertEquals(3, lp.compareAllRoutes(lp2).removed.size());
- // Check remove works
- lp.removeRoute(new RouteInfo(prefix, address, null));
+ // Remove route with incorrect interface, no route removed.
+ lp.removeRoute(new RouteInfo(prefix2, null, null));
assertEquals(3, lp.getRoutes().size());
- lp.removeRoute(new RouteInfo(prefix, address, "wlan0"));
+
+ // Check remove works when interface is correct.
+ lp.removeRoute(new RouteInfo(prefix2, null, "wlan0"));
assertEquals(2, lp.getRoutes().size());
assertAllRoutesHaveInterface("wlan0", lp);
assertAllRoutesNotHaveInterface("p2p0", lp);
diff --git a/tests/net/integration/src/com/android/server/net/integrationtests/TestNetworkStackService.kt b/tests/net/integration/src/com/android/server/net/integrationtests/TestNetworkStackService.kt
index 490c4679..23caf49 100644
--- a/tests/net/integration/src/com/android/server/net/integrationtests/TestNetworkStackService.kt
+++ b/tests/net/integration/src/com/android/server/net/integrationtests/TestNetworkStackService.kt
@@ -26,6 +26,7 @@
import android.os.IBinder
import com.android.networkstack.metrics.DataStallStatsUtils
import com.android.networkstack.netlink.TcpSocketTracker
+import com.android.server.NetworkStackService
import com.android.server.NetworkStackService.NetworkMonitorConnector
import com.android.server.NetworkStackService.NetworkStackConnector
import com.android.server.connectivity.NetworkMonitor
@@ -88,6 +89,7 @@
val nm = NetworkMonitor(this@TestNetworkStackService, cb,
this.network,
mock(IpConnectivityLog::class.java), mock(SharedLog::class.java),
+ mock(NetworkStackService.NetworkStackServiceManager::class.java),
NetworkMonitorDeps(privateDnsBypassNetwork),
mock(DataStallStatsUtils::class.java),
mock(TcpSocketTracker::class.java))
diff --git a/tests/net/java/android/net/Ikev2VpnProfileTest.java b/tests/net/java/android/net/Ikev2VpnProfileTest.java
index d6a2176..2273bc6 100644
--- a/tests/net/java/android/net/Ikev2VpnProfileTest.java
+++ b/tests/net/java/android/net/Ikev2VpnProfileTest.java
@@ -22,7 +22,6 @@
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
-import static org.mockito.Mockito.mock;
import android.test.mock.MockContext;
@@ -232,10 +231,12 @@
builder.setAuthDigitalSignature(mUserCert, mPrivateKey, mServerRootCa);
final VpnProfile profile = builder.build().toVpnProfile();
+ final String expectedSecret = Ikev2VpnProfile.PREFIX_INLINE
+ + Ikev2VpnProfile.encodeForIpsecSecret(mPrivateKey.getEncoded());
verifyVpnProfileCommon(profile);
assertEquals(Ikev2VpnProfile.certificateToPemString(mUserCert), profile.ipsecUserCert);
assertEquals(
- Ikev2VpnProfile.encodeForIpsecSecret(mPrivateKey.getEncoded()),
+ expectedSecret,
profile.ipsecSecret);
assertEquals(Ikev2VpnProfile.certificateToPemString(mServerRootCa), profile.ipsecCaCert);
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index 6fb4612..141e68a 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -78,6 +78,7 @@
import static android.net.NetworkPolicyManager.RULE_REJECT_ALL;
import static android.net.NetworkPolicyManager.RULE_REJECT_METERED;
import static android.net.RouteInfo.RTN_UNREACHABLE;
+import static android.system.OsConstants.IPPROTO_TCP;
import static com.android.server.ConnectivityServiceTestUtilsKt.transportToLegacyType;
import static com.android.testutils.ConcurrentUtilsKt.await;
@@ -138,6 +139,7 @@
import android.content.res.Resources;
import android.location.LocationManager;
import android.net.CaptivePortalData;
+import android.net.ConnectionInfo;
import android.net.ConnectivityManager;
import android.net.ConnectivityManager.NetworkCallback;
import android.net.ConnectivityManager.PacketKeepalive;
@@ -153,6 +155,7 @@
import android.net.INetworkPolicyListener;
import android.net.INetworkPolicyManager;
import android.net.INetworkStatsService;
+import android.net.InetAddresses;
import android.net.InterfaceConfiguration;
import android.net.IpPrefix;
import android.net.IpSecManager;
@@ -176,6 +179,7 @@
import android.net.SocketKeepalive;
import android.net.UidRange;
import android.net.Uri;
+import android.net.VpnManager;
import android.net.metrics.IpConnectivityLog;
import android.net.shared.NetworkMonitorUtils;
import android.net.shared.PrivateDnsConfig;
@@ -200,6 +204,7 @@
import android.os.UserHandle;
import android.os.UserManager;
import android.provider.Settings;
+import android.security.KeyStore;
import android.system.Os;
import android.test.mock.MockContentResolver;
import android.text.TextUtils;
@@ -272,6 +277,7 @@
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Predicate;
+import java.util.function.Supplier;
import kotlin.reflect.KClass;
@@ -445,15 +451,21 @@
return mPackageManager;
}
+ private int checkMockedPermission(String permission, Supplier<Integer> ifAbsent) {
+ final Integer granted = mMockedPermissions.get(permission);
+ return granted != null ? granted : ifAbsent.get();
+ }
+
@Override
public int checkPermission(String permission, int pid, int uid) {
- final Integer granted = mMockedPermissions.get(permission);
- if (granted == null) {
- // All non-mocked permissions should be held by the test or unnecessary: check as
- // normal to make sure the code does not rely on unexpected permissions.
- return super.checkPermission(permission, pid, uid);
- }
- return granted;
+ return checkMockedPermission(
+ permission, () -> super.checkPermission(permission, pid, uid));
+ }
+
+ @Override
+ public int checkCallingOrSelfPermission(String permission) {
+ return checkMockedPermission(
+ permission, () -> super.checkCallingOrSelfPermission(permission));
}
@Override
@@ -1002,12 +1014,13 @@
// Careful ! This is different from mNetworkAgent, because MockNetworkAgent does
// not inherit from NetworkAgent.
private TestNetworkAgentWrapper mMockNetworkAgent;
+ private int mVpnType = VpnManager.TYPE_VPN_SERVICE;
private VpnInfo mVpnInfo;
public MockVpn(int userId) {
super(startHandlerThreadAndReturnLooper(), mServiceContext, mNetworkManagementService,
- userId);
+ userId, mock(KeyStore.class));
}
public void setNetworkAgent(TestNetworkAgentWrapper agent) {
@@ -1022,6 +1035,10 @@
updateCapabilities(null /* defaultNetwork */);
}
+ public void setVpnType(int vpnType) {
+ mVpnType = vpnType;
+ }
+
@Override
public int getNetId() {
if (mMockNetworkAgent == null) {
@@ -1040,6 +1057,11 @@
return mConnected; // Similar trickery
}
+ @Override
+ public int getActiveAppVpnType() {
+ return mVpnType;
+ }
+
private void connect(boolean isAlwaysMetered) {
mNetworkCapabilities.set(mMockNetworkAgent.getNetworkCapabilities());
mConnected = true;
@@ -1158,6 +1180,10 @@
Arrays.asList(new UserInfo[] {
new UserInfo(VPN_USER, "", 0),
}));
+ final ApplicationInfo applicationInfo = new ApplicationInfo();
+ applicationInfo.targetSdkVersion = Build.VERSION_CODES.Q;
+ when(mPackageManager.getApplicationInfoAsUser(anyString(), anyInt(), any()))
+ .thenReturn(applicationInfo);
// InstrumentationTestRunner prepares a looper, but AndroidJUnitRunner does not.
// http://b/25897652 .
@@ -3019,7 +3045,7 @@
networkCapabilities.addTransportType(TRANSPORT_WIFI)
.setNetworkSpecifier(new MatchAllNetworkSpecifier());
mService.requestNetwork(networkCapabilities, null, 0, null,
- ConnectivityManager.TYPE_WIFI, TEST_PACKAGE_NAME);
+ ConnectivityManager.TYPE_WIFI, mContext.getPackageName());
});
class NonParcelableSpecifier extends NetworkSpecifier {
@@ -5901,6 +5927,12 @@
final LinkAddress myIpv6 = new LinkAddress("2001:db8:1::1/64");
final String kNat64PrefixString = "2001:db8:64:64:64:64::";
final IpPrefix kNat64Prefix = new IpPrefix(InetAddress.getByName(kNat64PrefixString), 96);
+ final RouteInfo defaultRoute = new RouteInfo((IpPrefix) null, myIpv6.getAddress(),
+ MOBILE_IFNAME);
+ final RouteInfo hostRoute = new RouteInfo(myIpv6, null, MOBILE_IFNAME);
+ final RouteInfo ipv4Default = new RouteInfo(myIpv4, null, MOBILE_IFNAME);
+ final RouteInfo stackedDefault = new RouteInfo((IpPrefix) null, myIpv4.getAddress(),
+ CLAT_PREFIX + MOBILE_IFNAME);
final NetworkRequest networkRequest = new NetworkRequest.Builder()
.addTransportType(TRANSPORT_CELLULAR)
@@ -5913,15 +5945,13 @@
final LinkProperties cellLp = new LinkProperties();
cellLp.setInterfaceName(MOBILE_IFNAME);
cellLp.addLinkAddress(myIpv6);
- cellLp.addRoute(new RouteInfo((IpPrefix) null, myIpv6.getAddress(), MOBILE_IFNAME));
- cellLp.addRoute(new RouteInfo(myIpv6, null, MOBILE_IFNAME));
+ cellLp.addRoute(defaultRoute);
+ cellLp.addRoute(hostRoute);
mCellNetworkAgent = new TestNetworkAgentWrapper(TRANSPORT_CELLULAR, cellLp);
reset(mNetworkManagementService);
reset(mMockDnsResolver);
reset(mMockNetd);
reset(mBatteryStatsService);
- when(mNetworkManagementService.getInterfaceConfig(CLAT_PREFIX + MOBILE_IFNAME))
- .thenReturn(getClatInterfaceConfig(myIpv4));
// Connect with ipv6 link properties. Expect prefix discovery to be started.
mCellNetworkAgent.connect(true);
@@ -5929,6 +5959,8 @@
waitForIdle();
verify(mMockNetd, times(1)).networkCreatePhysical(eq(cellNetId), anyInt());
+ verify(mNetworkManagementService, times(1)).addRoute(eq(cellNetId), eq(defaultRoute));
+ verify(mNetworkManagementService, times(1)).addRoute(eq(cellNetId), eq(hostRoute));
verify(mMockDnsResolver, times(1)).createNetworkCache(eq(cellNetId));
verify(mBatteryStatsService).noteNetworkInterfaceType(cellLp.getInterfaceName(),
TYPE_MOBILE);
@@ -5954,12 +5986,14 @@
verifyNoMoreInteractions(mMockNetd);
verifyNoMoreInteractions(mMockDnsResolver);
+ reset(mNetworkManagementService);
reset(mMockNetd);
reset(mMockDnsResolver);
+ when(mNetworkManagementService.getInterfaceConfig(CLAT_PREFIX + MOBILE_IFNAME))
+ .thenReturn(getClatInterfaceConfig(myIpv4));
// Remove IPv4 address. Expect prefix discovery to be started again.
cellLp.removeLinkAddress(myIpv4);
- cellLp.removeRoute(new RouteInfo(myIpv4, null, MOBILE_IFNAME));
mCellNetworkAgent.sendLinkProperties(cellLp);
networkCallback.expectCallback(CallbackEntry.LINK_PROPERTIES_CHANGED, mCellNetworkAgent);
verify(mMockDnsResolver, times(1)).startPrefix64Discovery(cellNetId);
@@ -5981,6 +6015,7 @@
List<LinkProperties> stackedLps = mCm.getLinkProperties(mCellNetworkAgent.getNetwork())
.getStackedLinks();
assertEquals(makeClatLinkProperties(myIpv4), stackedLps.get(0));
+ verify(mNetworkManagementService).addRoute(eq(cellNetId), eq(stackedDefault));
// Change trivial linkproperties and see if stacked link is preserved.
cellLp.addDnsServer(InetAddress.getByName("8.8.8.8"));
@@ -6006,8 +6041,9 @@
// Add ipv4 address, expect that clatd and prefix discovery are stopped and stacked
// linkproperties are cleaned up.
cellLp.addLinkAddress(myIpv4);
- cellLp.addRoute(new RouteInfo(myIpv4, null, MOBILE_IFNAME));
+ cellLp.addRoute(ipv4Default);
mCellNetworkAgent.sendLinkProperties(cellLp);
+ verify(mNetworkManagementService).addRoute(eq(cellNetId), eq(stackedDefault));
networkCallback.expectCallback(CallbackEntry.LINK_PROPERTIES_CHANGED, mCellNetworkAgent);
verify(mMockNetd, times(1)).clatdStop(MOBILE_IFNAME);
verify(mMockDnsResolver, times(1)).stopPrefix64Discovery(cellNetId);
@@ -6026,8 +6062,11 @@
verifyNoMoreInteractions(mMockNetd);
verifyNoMoreInteractions(mMockDnsResolver);
+ reset(mNetworkManagementService);
reset(mMockNetd);
reset(mMockDnsResolver);
+ when(mNetworkManagementService.getInterfaceConfig(CLAT_PREFIX + MOBILE_IFNAME))
+ .thenReturn(getClatInterfaceConfig(myIpv4));
// Stopping prefix discovery causes netd to tell us that the NAT64 prefix is gone.
mService.mNetdEventCallback.onNat64PrefixEvent(cellNetId, false /* added */,
@@ -6041,6 +6080,7 @@
cellLp.removeDnsServer(InetAddress.getByName("8.8.8.8"));
mCellNetworkAgent.sendLinkProperties(cellLp);
networkCallback.expectCallback(CallbackEntry.LINK_PROPERTIES_CHANGED, mCellNetworkAgent);
+ verify(mNetworkManagementService, times(1)).removeRoute(eq(cellNetId), eq(ipv4Default));
verify(mMockDnsResolver, times(1)).startPrefix64Discovery(cellNetId);
mService.mNetdEventCallback.onNat64PrefixEvent(cellNetId, true /* added */,
kNat64PrefixString, 96);
@@ -6416,17 +6456,173 @@
assertEquals(wifiLp, mService.getActiveLinkProperties());
}
+ private void setupLocationPermissions(
+ int targetSdk, boolean locationToggle, String op, String perm) throws Exception {
+ final ApplicationInfo applicationInfo = new ApplicationInfo();
+ applicationInfo.targetSdkVersion = targetSdk;
+ when(mPackageManager.getApplicationInfoAsUser(anyString(), anyInt(), any()))
+ .thenReturn(applicationInfo);
+
+ when(mLocationManager.isLocationEnabledForUser(any())).thenReturn(locationToggle);
+
+ if (op != null) {
+ when(mAppOpsManager.noteOp(eq(op), eq(Process.myUid()), eq(mContext.getPackageName())))
+ .thenReturn(AppOpsManager.MODE_ALLOWED);
+ }
+
+ if (perm != null) {
+ mServiceContext.setPermission(perm, PERMISSION_GRANTED);
+ }
+ }
+
+ private int getOwnerUidNetCapsForCallerPermission(int ownerUid, int callerUid) {
+ final NetworkCapabilities netCap = new NetworkCapabilities().setOwnerUid(ownerUid);
+
+ return mService
+ .maybeSanitizeLocationInfoForCaller(netCap, callerUid, mContext.getPackageName())
+ .getOwnerUid();
+ }
+
@Test
- public void testNetworkCapabilitiesRestrictedForCallerPermissions() {
- int callerUid = Process.myUid();
- final NetworkCapabilities originalNc = new NetworkCapabilities();
- originalNc.setOwnerUid(callerUid);
+ public void testMaybeSanitizeLocationInfoForCallerWithFineLocationAfterQ() throws Exception {
+ setupLocationPermissions(Build.VERSION_CODES.Q, true, AppOpsManager.OPSTR_FINE_LOCATION,
+ Manifest.permission.ACCESS_FINE_LOCATION);
- final NetworkCapabilities newNc =
- mService.networkCapabilitiesRestrictedForCallerPermissions(
- originalNc, Process.myPid(), callerUid);
+ final int myUid = Process.myUid();
+ assertEquals(myUid, getOwnerUidNetCapsForCallerPermission(myUid, myUid));
+ }
- assertEquals(Process.INVALID_UID, newNc.getOwnerUid());
+ @Test
+ public void testMaybeSanitizeLocationInfoForCallerWithCoarseLocationPreQ() throws Exception {
+ setupLocationPermissions(Build.VERSION_CODES.P, true, AppOpsManager.OPSTR_COARSE_LOCATION,
+ Manifest.permission.ACCESS_COARSE_LOCATION);
+
+ final int myUid = Process.myUid();
+ assertEquals(myUid, getOwnerUidNetCapsForCallerPermission(myUid, myUid));
+ }
+
+ @Test
+ public void testMaybeSanitizeLocationInfoForCallerLocationOff() throws Exception {
+ // Test that even with fine location permission, and UIDs matching, the UID is sanitized.
+ setupLocationPermissions(Build.VERSION_CODES.Q, false, AppOpsManager.OPSTR_FINE_LOCATION,
+ Manifest.permission.ACCESS_FINE_LOCATION);
+
+ final int myUid = Process.myUid();
+ assertEquals(Process.INVALID_UID, getOwnerUidNetCapsForCallerPermission(myUid, myUid));
+ }
+
+ @Test
+ public void testMaybeSanitizeLocationInfoForCallerWrongUid() throws Exception {
+ // Test that even with fine location permission, not being the owner leads to sanitization.
+ setupLocationPermissions(Build.VERSION_CODES.Q, true, AppOpsManager.OPSTR_FINE_LOCATION,
+ Manifest.permission.ACCESS_FINE_LOCATION);
+
+ final int myUid = Process.myUid();
+ assertEquals(Process.INVALID_UID, getOwnerUidNetCapsForCallerPermission(myUid + 1, myUid));
+ }
+
+ @Test
+ public void testMaybeSanitizeLocationInfoForCallerWithCoarseLocationAfterQ() throws Exception {
+ // Test that not having fine location permission leads to sanitization.
+ setupLocationPermissions(Build.VERSION_CODES.Q, true, AppOpsManager.OPSTR_COARSE_LOCATION,
+ Manifest.permission.ACCESS_COARSE_LOCATION);
+
+ // Test that without the location permission, the owner field is sanitized.
+ final int myUid = Process.myUid();
+ assertEquals(Process.INVALID_UID, getOwnerUidNetCapsForCallerPermission(myUid, myUid));
+ }
+
+ @Test
+ public void testMaybeSanitizeLocationInfoForCallerWithoutLocationPermission() throws Exception {
+ setupLocationPermissions(Build.VERSION_CODES.Q, true, null /* op */, null /* perm */);
+
+ // Test that without the location permission, the owner field is sanitized.
+ final int myUid = Process.myUid();
+ assertEquals(Process.INVALID_UID, getOwnerUidNetCapsForCallerPermission(myUid, myUid));
+ }
+
+ private void setupConnectionOwnerUid(int vpnOwnerUid, @VpnManager.VpnType int vpnType)
+ throws Exception {
+ final Set<UidRange> vpnRange = Collections.singleton(UidRange.createForUser(VPN_USER));
+ establishVpn(new LinkProperties(), vpnOwnerUid, vpnRange);
+ mMockVpn.setVpnType(vpnType);
+
+ final VpnInfo vpnInfo = new VpnInfo();
+ vpnInfo.ownerUid = vpnOwnerUid;
+ mMockVpn.setVpnInfo(vpnInfo);
+ }
+
+ private void setupConnectionOwnerUidAsVpnApp(int vpnOwnerUid, @VpnManager.VpnType int vpnType)
+ throws Exception {
+ setupConnectionOwnerUid(vpnOwnerUid, vpnType);
+
+ // Test as VPN app
+ mServiceContext.setPermission(android.Manifest.permission.NETWORK_STACK, PERMISSION_DENIED);
+ mServiceContext.setPermission(
+ NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, PERMISSION_DENIED);
+ }
+
+ private ConnectionInfo getTestConnectionInfo() throws Exception {
+ return new ConnectionInfo(
+ IPPROTO_TCP,
+ new InetSocketAddress(InetAddresses.parseNumericAddress("1.2.3.4"), 1234),
+ new InetSocketAddress(InetAddresses.parseNumericAddress("2.3.4.5"), 2345));
+ }
+
+ @Test
+ public void testGetConnectionOwnerUidPlatformVpn() throws Exception {
+ final int myUid = Process.myUid();
+ setupConnectionOwnerUidAsVpnApp(myUid, VpnManager.TYPE_VPN_PLATFORM);
+
+ try {
+ mService.getConnectionOwnerUid(getTestConnectionInfo());
+ fail("Expected SecurityException for non-VpnService app");
+ } catch (SecurityException expected) {
+ }
+ }
+
+ @Test
+ public void testGetConnectionOwnerUidVpnServiceWrongUser() throws Exception {
+ final int myUid = Process.myUid();
+ setupConnectionOwnerUidAsVpnApp(myUid + 1, VpnManager.TYPE_VPN_SERVICE);
+
+ try {
+ mService.getConnectionOwnerUid(getTestConnectionInfo());
+ fail("Expected SecurityException for non-VpnService app");
+ } catch (SecurityException expected) {
+ }
+ }
+
+ @Test
+ public void testGetConnectionOwnerUidVpnServiceDoesNotThrow() throws Exception {
+ final int myUid = Process.myUid();
+ setupConnectionOwnerUidAsVpnApp(myUid, VpnManager.TYPE_VPN_SERVICE);
+
+ // TODO: Test the returned UID
+ mService.getConnectionOwnerUid(getTestConnectionInfo());
+ }
+
+ @Test
+ public void testGetConnectionOwnerUidVpnServiceNetworkStackDoesNotThrow() throws Exception {
+ final int myUid = Process.myUid();
+ setupConnectionOwnerUid(myUid, VpnManager.TYPE_VPN_SERVICE);
+ mServiceContext.setPermission(
+ android.Manifest.permission.NETWORK_STACK, PERMISSION_GRANTED);
+
+ // TODO: Test the returned UID
+ mService.getConnectionOwnerUid(getTestConnectionInfo());
+ }
+
+ @Test
+ public void testGetConnectionOwnerUidVpnServiceMainlineNetworkStackDoesNotThrow()
+ throws Exception {
+ final int myUid = Process.myUid();
+ setupConnectionOwnerUid(myUid, VpnManager.TYPE_VPN_SERVICE);
+ mServiceContext.setPermission(
+ NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, PERMISSION_GRANTED);
+
+ // TODO: Test the returned UID
+ mService.getConnectionOwnerUid(getTestConnectionInfo());
}
private TestNetworkAgentWrapper establishVpn(
@@ -6628,21 +6824,6 @@
mContext.getOpPackageName()));
}
- private void setupLocationPermissions(
- int targetSdk, boolean locationToggle, String op, String perm) throws Exception {
- final ApplicationInfo applicationInfo = new ApplicationInfo();
- applicationInfo.targetSdkVersion = targetSdk;
- when(mPackageManager.getApplicationInfoAsUser(anyString(), anyInt(), any()))
- .thenReturn(applicationInfo);
-
- when(mLocationManager.isLocationEnabledForUser(any())).thenReturn(locationToggle);
-
- when(mAppOpsManager.noteOp(eq(op), eq(Process.myUid()), eq(mContext.getPackageName())))
- .thenReturn(AppOpsManager.MODE_ALLOWED);
-
- mServiceContext.setPermission(perm, PERMISSION_GRANTED);
- }
-
private void setUpConnectivityDiagnosticsCallback() throws Exception {
final NetworkRequest request = new NetworkRequest.Builder().build();
when(mConnectivityDiagnosticsCallback.asBinder()).thenReturn(mIBinder);
diff --git a/tests/net/java/com/android/server/connectivity/VpnTest.java b/tests/net/java/com/android/server/connectivity/VpnTest.java
index eb78529..1994d1f 100644
--- a/tests/net/java/com/android/server/connectivity/VpnTest.java
+++ b/tests/net/java/com/android/server/connectivity/VpnTest.java
@@ -59,9 +59,15 @@
import android.content.pm.UserInfo;
import android.content.res.Resources;
import android.net.ConnectivityManager;
+import android.net.Ikev2VpnProfile;
+import android.net.InetAddresses;
+import android.net.IpPrefix;
+import android.net.IpSecManager;
+import android.net.LinkProperties;
import android.net.Network;
import android.net.NetworkCapabilities;
import android.net.NetworkInfo.DetailedState;
+import android.net.RouteInfo;
import android.net.UidRange;
import android.net.VpnManager;
import android.net.VpnService;
@@ -72,6 +78,7 @@
import android.os.Process;
import android.os.UserHandle;
import android.os.UserManager;
+import android.provider.Settings;
import android.security.Credentials;
import android.security.KeyStore;
import android.util.ArrayMap;
@@ -83,6 +90,7 @@
import com.android.internal.R;
import com.android.internal.net.VpnConfig;
import com.android.internal.net.VpnProfile;
+import com.android.server.IpSecService;
import org.junit.Before;
import org.junit.Test;
@@ -92,6 +100,7 @@
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import java.net.Inet4Address;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
@@ -124,6 +133,9 @@
}
static final String TEST_VPN_PKG = "com.dummy.vpn";
+ private static final String TEST_VPN_SERVER = "1.2.3.4";
+ private static final String TEST_VPN_IDENTITY = "identity";
+ private static final byte[] TEST_VPN_PSK = "psk".getBytes();
/**
* Names and UIDs for some fake packages. Important points:
@@ -150,23 +162,39 @@
@Mock private Vpn.SystemServices mSystemServices;
@Mock private Vpn.Ikev2SessionCreator mIkev2SessionCreator;
@Mock private ConnectivityManager mConnectivityManager;
+ @Mock private IpSecService mIpSecService;
@Mock private KeyStore mKeyStore;
- private final VpnProfile mVpnProfile = new VpnProfile("key");
+ private final VpnProfile mVpnProfile;
+
+ private IpSecManager mIpSecManager;
+
+ public VpnTest() throws Exception {
+ // Build an actual VPN profile that is capable of being converted to and from an
+ // Ikev2VpnProfile
+ final Ikev2VpnProfile.Builder builder =
+ new Ikev2VpnProfile.Builder(TEST_VPN_SERVER, TEST_VPN_IDENTITY);
+ builder.setAuthPsk(TEST_VPN_PSK);
+ mVpnProfile = builder.build().toVpnProfile();
+ }
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
+ mIpSecManager = new IpSecManager(mContext, mIpSecService);
+
when(mContext.getPackageManager()).thenReturn(mPackageManager);
setMockedPackages(mPackages);
- when(mContext.getPackageName()).thenReturn(Vpn.class.getPackage().getName());
+ when(mContext.getPackageName()).thenReturn(TEST_VPN_PKG);
+ when(mContext.getOpPackageName()).thenReturn(TEST_VPN_PKG);
when(mContext.getSystemService(eq(Context.USER_SERVICE))).thenReturn(mUserManager);
when(mContext.getSystemService(eq(Context.APP_OPS_SERVICE))).thenReturn(mAppOps);
when(mContext.getSystemService(eq(Context.NOTIFICATION_SERVICE)))
.thenReturn(mNotificationManager);
when(mContext.getSystemService(eq(Context.CONNECTIVITY_SERVICE)))
.thenReturn(mConnectivityManager);
+ when(mContext.getSystemService(eq(Context.IPSEC_SERVICE))).thenReturn(mIpSecManager);
when(mContext.getString(R.string.config_customVpnAlwaysOnDisconnectedDialogComponent))
.thenReturn(Resources.getSystem().getString(
R.string.config_customVpnAlwaysOnDisconnectedDialogComponent));
@@ -260,17 +288,17 @@
assertFalse(vpn.getLockdown());
// Set always-on without lockdown.
- assertTrue(vpn.setAlwaysOnPackage(PKGS[1], false, Collections.emptyList()));
+ assertTrue(vpn.setAlwaysOnPackage(PKGS[1], false, Collections.emptyList(), mKeyStore));
assertTrue(vpn.getAlwaysOn());
assertFalse(vpn.getLockdown());
// Set always-on with lockdown.
- assertTrue(vpn.setAlwaysOnPackage(PKGS[1], true, Collections.emptyList()));
+ assertTrue(vpn.setAlwaysOnPackage(PKGS[1], true, Collections.emptyList(), mKeyStore));
assertTrue(vpn.getAlwaysOn());
assertTrue(vpn.getLockdown());
// Remove always-on configuration.
- assertTrue(vpn.setAlwaysOnPackage(null, false, Collections.emptyList()));
+ assertTrue(vpn.setAlwaysOnPackage(null, false, Collections.emptyList(), mKeyStore));
assertFalse(vpn.getAlwaysOn());
assertFalse(vpn.getLockdown());
}
@@ -284,11 +312,11 @@
assertUnblocked(vpn, user.start + PKG_UIDS[0], user.start + PKG_UIDS[1], user.start + PKG_UIDS[2], user.start + PKG_UIDS[3]);
// Set always-on without lockdown.
- assertTrue(vpn.setAlwaysOnPackage(PKGS[1], false, null));
+ assertTrue(vpn.setAlwaysOnPackage(PKGS[1], false, null, mKeyStore));
assertUnblocked(vpn, user.start + PKG_UIDS[0], user.start + PKG_UIDS[1], user.start + PKG_UIDS[2], user.start + PKG_UIDS[3]);
// Set always-on with lockdown.
- assertTrue(vpn.setAlwaysOnPackage(PKGS[1], true, null));
+ assertTrue(vpn.setAlwaysOnPackage(PKGS[1], true, null, mKeyStore));
verify(mNetService).setAllowOnlyVpnForUids(eq(true), aryEq(new UidRange[] {
new UidRange(user.start, user.start + PKG_UIDS[1] - 1),
new UidRange(user.start + PKG_UIDS[1] + 1, user.stop)
@@ -297,7 +325,7 @@
assertUnblocked(vpn, user.start + PKG_UIDS[1]);
// Switch to another app.
- assertTrue(vpn.setAlwaysOnPackage(PKGS[3], true, null));
+ assertTrue(vpn.setAlwaysOnPackage(PKGS[3], true, null, mKeyStore));
verify(mNetService).setAllowOnlyVpnForUids(eq(false), aryEq(new UidRange[] {
new UidRange(user.start, user.start + PKG_UIDS[1] - 1),
new UidRange(user.start + PKG_UIDS[1] + 1, user.stop)
@@ -316,7 +344,8 @@
final UidRange user = UidRange.createForUser(primaryUser.id);
// Set always-on with lockdown and whitelist app PKGS[2] from lockdown.
- assertTrue(vpn.setAlwaysOnPackage(PKGS[1], true, Collections.singletonList(PKGS[2])));
+ assertTrue(vpn.setAlwaysOnPackage(
+ PKGS[1], true, Collections.singletonList(PKGS[2]), mKeyStore));
verify(mNetService).setAllowOnlyVpnForUids(eq(true), aryEq(new UidRange[] {
new UidRange(user.start, user.start + PKG_UIDS[1] - 1),
new UidRange(user.start + PKG_UIDS[2] + 1, user.stop)
@@ -325,7 +354,8 @@
assertUnblocked(vpn, user.start + PKG_UIDS[1], user.start + PKG_UIDS[2]);
// Change whitelisted app to PKGS[3].
- assertTrue(vpn.setAlwaysOnPackage(PKGS[1], true, Collections.singletonList(PKGS[3])));
+ assertTrue(vpn.setAlwaysOnPackage(
+ PKGS[1], true, Collections.singletonList(PKGS[3]), mKeyStore));
verify(mNetService).setAllowOnlyVpnForUids(eq(false), aryEq(new UidRange[] {
new UidRange(user.start + PKG_UIDS[2] + 1, user.stop)
}));
@@ -337,7 +367,8 @@
assertUnblocked(vpn, user.start + PKG_UIDS[1], user.start + PKG_UIDS[3]);
// Change the VPN app.
- assertTrue(vpn.setAlwaysOnPackage(PKGS[0], true, Collections.singletonList(PKGS[3])));
+ assertTrue(vpn.setAlwaysOnPackage(
+ PKGS[0], true, Collections.singletonList(PKGS[3]), mKeyStore));
verify(mNetService).setAllowOnlyVpnForUids(eq(false), aryEq(new UidRange[] {
new UidRange(user.start, user.start + PKG_UIDS[1] - 1),
new UidRange(user.start + PKG_UIDS[1] + 1, user.start + PKG_UIDS[3] - 1)
@@ -350,7 +381,7 @@
assertUnblocked(vpn, user.start + PKG_UIDS[0], user.start + PKG_UIDS[3]);
// Remove the whitelist.
- assertTrue(vpn.setAlwaysOnPackage(PKGS[0], true, null));
+ assertTrue(vpn.setAlwaysOnPackage(PKGS[0], true, null, mKeyStore));
verify(mNetService).setAllowOnlyVpnForUids(eq(false), aryEq(new UidRange[] {
new UidRange(user.start + PKG_UIDS[0] + 1, user.start + PKG_UIDS[3] - 1),
new UidRange(user.start + PKG_UIDS[3] + 1, user.stop)
@@ -363,7 +394,8 @@
assertUnblocked(vpn, user.start + PKG_UIDS[0]);
// Add the whitelist.
- assertTrue(vpn.setAlwaysOnPackage(PKGS[0], true, Collections.singletonList(PKGS[1])));
+ assertTrue(vpn.setAlwaysOnPackage(
+ PKGS[0], true, Collections.singletonList(PKGS[1]), mKeyStore));
verify(mNetService).setAllowOnlyVpnForUids(eq(false), aryEq(new UidRange[] {
new UidRange(user.start + PKG_UIDS[0] + 1, user.stop)
}));
@@ -375,12 +407,13 @@
assertUnblocked(vpn, user.start + PKG_UIDS[0], user.start + PKG_UIDS[1]);
// Try whitelisting a package with a comma, should be rejected.
- assertFalse(vpn.setAlwaysOnPackage(PKGS[0], true, Collections.singletonList("a.b,c.d")));
+ assertFalse(vpn.setAlwaysOnPackage(
+ PKGS[0], true, Collections.singletonList("a.b,c.d"), mKeyStore));
// Pass a non-existent packages in the whitelist, they (and only they) should be ignored.
// Whitelisted package should change from PGKS[1] to PKGS[2].
- assertTrue(vpn.setAlwaysOnPackage(PKGS[0], true,
- Arrays.asList("com.foo.app", PKGS[2], "com.bar.app")));
+ assertTrue(vpn.setAlwaysOnPackage(
+ PKGS[0], true, Arrays.asList("com.foo.app", PKGS[2], "com.bar.app"), mKeyStore));
verify(mNetService).setAllowOnlyVpnForUids(eq(false), aryEq(new UidRange[]{
new UidRange(user.start + PKG_UIDS[0] + 1, user.start + PKG_UIDS[1] - 1),
new UidRange(user.start + PKG_UIDS[1] + 1, user.stop)
@@ -405,7 +438,7 @@
final UidRange profile = UidRange.createForUser(tempProfile.id);
// Set lockdown.
- assertTrue(vpn.setAlwaysOnPackage(PKGS[3], true, null));
+ assertTrue(vpn.setAlwaysOnPackage(PKGS[3], true, null, mKeyStore));
verify(mNetService).setAllowOnlyVpnForUids(eq(true), aryEq(new UidRange[] {
new UidRange(user.start, user.start + PKG_UIDS[3] - 1),
new UidRange(user.start + PKG_UIDS[3] + 1, user.stop)
@@ -499,22 +532,22 @@
.thenReturn(Collections.singletonList(resInfo));
// null package name should return false
- assertFalse(vpn.isAlwaysOnPackageSupported(null));
+ assertFalse(vpn.isAlwaysOnPackageSupported(null, mKeyStore));
// Pre-N apps are not supported
appInfo.targetSdkVersion = VERSION_CODES.M;
- assertFalse(vpn.isAlwaysOnPackageSupported(PKGS[0]));
+ assertFalse(vpn.isAlwaysOnPackageSupported(PKGS[0], mKeyStore));
// N+ apps are supported by default
appInfo.targetSdkVersion = VERSION_CODES.N;
- assertTrue(vpn.isAlwaysOnPackageSupported(PKGS[0]));
+ assertTrue(vpn.isAlwaysOnPackageSupported(PKGS[0], mKeyStore));
// Apps that opt out explicitly are not supported
appInfo.targetSdkVersion = VERSION_CODES.CUR_DEVELOPMENT;
Bundle metaData = new Bundle();
metaData.putBoolean(VpnService.SERVICE_META_DATA_SUPPORTS_ALWAYS_ON, false);
svcInfo.metaData = metaData;
- assertFalse(vpn.isAlwaysOnPackageSupported(PKGS[0]));
+ assertFalse(vpn.isAlwaysOnPackageSupported(PKGS[0], mKeyStore));
}
@Test
@@ -531,7 +564,7 @@
.cancelAsUser(anyString(), anyInt(), eq(userHandle));
// Start showing a notification for disconnected once always-on.
- vpn.setAlwaysOnPackage(PKGS[0], false, null);
+ vpn.setAlwaysOnPackage(PKGS[0], false, null, mKeyStore);
order.verify(mNotificationManager)
.notifyAsUser(anyString(), anyInt(), any(), eq(userHandle));
@@ -545,7 +578,7 @@
.notifyAsUser(anyString(), anyInt(), any(), eq(userHandle));
// Notification should be cleared after unsetting always-on package.
- vpn.setAlwaysOnPackage(null, false, null);
+ vpn.setAlwaysOnPackage(null, false, null, mKeyStore);
order.verify(mNotificationManager).cancelAsUser(anyString(), anyInt(), eq(userHandle));
}
@@ -656,8 +689,12 @@
}
private Vpn createVpnAndSetupUidChecks(int... grantedOps) throws Exception {
- final Vpn vpn = createVpn(primaryUser.id);
- setMockedUsers(primaryUser);
+ return createVpnAndSetupUidChecks(primaryUser, grantedOps);
+ }
+
+ private Vpn createVpnAndSetupUidChecks(UserInfo user, int... grantedOps) throws Exception {
+ final Vpn vpn = createVpn(user.id);
+ setMockedUsers(user);
when(mPackageManager.getPackageUidAsUser(eq(TEST_VPN_PKG), anyInt()))
.thenReturn(Process.myUid());
@@ -726,6 +763,19 @@
}
@Test
+ public void testProvisionVpnProfileRestrictedUser() throws Exception {
+ final Vpn vpn =
+ createVpnAndSetupUidChecks(
+ restrictedProfileA, AppOpsManager.OP_ACTIVATE_PLATFORM_VPN);
+
+ try {
+ vpn.provisionVpnProfile(TEST_VPN_PKG, mVpnProfile, mKeyStore);
+ fail("Expected SecurityException due to restricted user");
+ } catch (SecurityException expected) {
+ }
+ }
+
+ @Test
public void testDeleteVpnProfile() throws Exception {
final Vpn vpn = createVpnAndSetupUidChecks();
@@ -736,6 +786,19 @@
}
@Test
+ public void testDeleteVpnProfileRestrictedUser() throws Exception {
+ final Vpn vpn =
+ createVpnAndSetupUidChecks(
+ restrictedProfileA, AppOpsManager.OP_ACTIVATE_PLATFORM_VPN);
+
+ try {
+ vpn.deleteVpnProfile(TEST_VPN_PKG, mKeyStore);
+ fail("Expected SecurityException due to restricted user");
+ } catch (SecurityException expected) {
+ }
+ }
+
+ @Test
public void testGetVpnProfilePrivileged() throws Exception {
final Vpn vpn = createVpnAndSetupUidChecks();
@@ -820,6 +883,32 @@
}
@Test
+ public void testStartVpnProfileRestrictedUser() throws Exception {
+ final Vpn vpn =
+ createVpnAndSetupUidChecks(
+ restrictedProfileA, AppOpsManager.OP_ACTIVATE_PLATFORM_VPN);
+
+ try {
+ vpn.startVpnProfile(TEST_VPN_PKG, mKeyStore);
+ fail("Expected SecurityException due to restricted user");
+ } catch (SecurityException expected) {
+ }
+ }
+
+ @Test
+ public void testStopVpnProfileRestrictedUser() throws Exception {
+ final Vpn vpn =
+ createVpnAndSetupUidChecks(
+ restrictedProfileA, AppOpsManager.OP_ACTIVATE_PLATFORM_VPN);
+
+ try {
+ vpn.stopVpnProfile(TEST_VPN_PKG);
+ fail("Expected SecurityException due to restricted user");
+ } catch (SecurityException expected) {
+ }
+ }
+
+ @Test
public void testSetPackageAuthorizationVpnService() throws Exception {
final Vpn vpn = createVpnAndSetupUidChecks();
@@ -864,12 +953,68 @@
eq(AppOpsManager.MODE_IGNORED));
}
+ private void setAndVerifyAlwaysOnPackage(Vpn vpn, int uid, boolean lockdownEnabled) {
+ assertTrue(vpn.setAlwaysOnPackage(TEST_VPN_PKG, lockdownEnabled, null, mKeyStore));
+
+ verify(mKeyStore).get(eq(vpn.getProfileNameForPackage(TEST_VPN_PKG)));
+ verify(mAppOps).setMode(
+ eq(AppOpsManager.OP_ACTIVATE_PLATFORM_VPN), eq(uid), eq(TEST_VPN_PKG),
+ eq(AppOpsManager.MODE_ALLOWED));
+
+ verify(mSystemServices).settingsSecurePutStringForUser(
+ eq(Settings.Secure.ALWAYS_ON_VPN_APP), eq(TEST_VPN_PKG), eq(primaryUser.id));
+ verify(mSystemServices).settingsSecurePutIntForUser(
+ eq(Settings.Secure.ALWAYS_ON_VPN_LOCKDOWN), eq(lockdownEnabled ? 1 : 0),
+ eq(primaryUser.id));
+ verify(mSystemServices).settingsSecurePutStringForUser(
+ eq(Settings.Secure.ALWAYS_ON_VPN_LOCKDOWN_WHITELIST), eq(""), eq(primaryUser.id));
+ }
+
+ @Test
+ public void testSetAndStartAlwaysOnVpn() throws Exception {
+ final Vpn vpn = createVpn(primaryUser.id);
+ setMockedUsers(primaryUser);
+
+ // UID checks must return a different UID; otherwise it'll be treated as already prepared.
+ final int uid = Process.myUid() + 1;
+ when(mPackageManager.getPackageUidAsUser(eq(TEST_VPN_PKG), anyInt()))
+ .thenReturn(uid);
+ when(mKeyStore.get(vpn.getProfileNameForPackage(TEST_VPN_PKG)))
+ .thenReturn(mVpnProfile.encode());
+
+ setAndVerifyAlwaysOnPackage(vpn, uid, false);
+ assertTrue(vpn.startAlwaysOnVpn(mKeyStore));
+
+ // TODO: Test the Ikev2VpnRunner started up properly. Relies on utility methods added in
+ // a subsequent CL.
+ }
+
+ @Test
+ public void testStartLegacyVpn() throws Exception {
+ final Vpn vpn = createVpn(primaryUser.id);
+ setMockedUsers(primaryUser);
+
+ // Dummy egress interface
+ final String egressIface = "DUMMY0";
+ final LinkProperties lp = new LinkProperties();
+ lp.setInterfaceName(egressIface);
+
+ final RouteInfo defaultRoute = new RouteInfo(new IpPrefix(Inet4Address.ANY, 0),
+ InetAddresses.parseNumericAddress("192.0.2.0"), egressIface);
+ lp.addRoute(defaultRoute);
+
+ vpn.startLegacyVpn(mVpnProfile, mKeyStore, lp);
+
+ // TODO: Test the Ikev2VpnRunner started up properly. Relies on utility methods added in
+ // a subsequent CL.
+ }
+
/**
* Mock some methods of vpn object.
*/
private Vpn createVpn(@UserIdInt int userId) {
return new Vpn(Looper.myLooper(), mContext, mNetService,
- userId, mSystemServices, mIkev2SessionCreator);
+ userId, mKeyStore, mSystemServices, mIkev2SessionCreator);
}
private static void assertBlocked(Vpn vpn, int... uids) {
diff --git a/tools/aapt/tests/AaptConfig_test.cpp b/tools/aapt/tests/AaptConfig_test.cpp
index 4f22fa5..b7c6bd2 100644
--- a/tools/aapt/tests/AaptConfig_test.cpp
+++ b/tools/aapt/tests/AaptConfig_test.cpp
@@ -20,7 +20,6 @@
#include "AaptConfig.h"
#include "ConfigDescription.h"
#include "SdkConstants.h"
-#include "TestHelper.h"
using android::String8;
@@ -127,4 +126,4 @@
config.colorMode & android::ResTable_config::MASK_HDR);
EXPECT_EQ(SDK_O, config.sdkVersion);
EXPECT_EQ(String8("lowdr-v26"), config.toString());
-}
\ No newline at end of file
+}
diff --git a/tools/aapt/tests/AaptGroupEntry_test.cpp b/tools/aapt/tests/AaptGroupEntry_test.cpp
index 7348a08..bf5ca59 100644
--- a/tools/aapt/tests/AaptGroupEntry_test.cpp
+++ b/tools/aapt/tests/AaptGroupEntry_test.cpp
@@ -19,7 +19,6 @@
#include "AaptAssets.h"
#include "ResourceFilter.h"
-#include "TestHelper.h"
using android::String8;
diff --git a/tools/aapt/tests/ResourceTable_test.cpp b/tools/aapt/tests/ResourceTable_test.cpp
index f2c696b..0d550df 100644
--- a/tools/aapt/tests/ResourceTable_test.cpp
+++ b/tools/aapt/tests/ResourceTable_test.cpp
@@ -19,7 +19,6 @@
#include "ConfigDescription.h"
#include "ResourceTable.h"
-#include "TestHelper.h"
using android::String16;
diff --git a/tools/aapt/tests/TestHelper.h b/tools/aapt/tests/TestHelper.h
deleted file mode 100644
index 79174832..0000000
--- a/tools/aapt/tests/TestHelper.h
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef __TEST_HELPER_H
-#define __TEST_HELPER_H
-
-#include <utils/String8.h>
-
-namespace android {
-
-/**
- * Stream operator for nicely printing String8's in gtest output.
- */
-inline std::ostream& operator<<(std::ostream& stream, const String8& str) {
- return stream << str.string();
-}
-
-}
-
-#endif
diff --git a/tools/hiddenapi/merge_csv.py b/tools/hiddenapi/merge_csv.py
index 9661927..6a5b0e1 100755
--- a/tools/hiddenapi/merge_csv.py
+++ b/tools/hiddenapi/merge_csv.py
@@ -14,26 +14,56 @@
# See the License for the specific language governing permissions and
# limitations under the License.
"""
-Merge mutliple CSV files, possibly with different columns, writing to stdout.
+Merge multiple CSV files, possibly with different columns.
"""
+import argparse
import csv
-import sys
+import io
-csv_readers = [
- csv.DictReader(open(csv_file, 'r'), delimiter=',', quotechar='|')
- for csv_file in sys.argv[1:]
-]
+from zipfile import ZipFile
-# Build union of all columns from source files:
+args_parser = argparse.ArgumentParser(description='Merge given CSV files into a single one.')
+args_parser.add_argument('--header', help='Comma separated field names; '
+ 'if missing determines the header from input files.')
+args_parser.add_argument('--zip_input', help='ZIP archive with all CSV files to merge.')
+args_parser.add_argument('--output', help='Output file for merged CSV.',
+ default='-', type=argparse.FileType('w'))
+args_parser.add_argument('files', nargs=argparse.REMAINDER)
+args = args_parser.parse_args()
+
+
+def dict_reader(input):
+ return csv.DictReader(input, delimiter=',', quotechar='|')
+
+
+if args.zip_input and len(args.files) > 0:
+ raise ValueError('Expecting either a single ZIP with CSV files'
+ ' or a list of CSV files as input; not both.')
+
+csv_readers = []
+if len(args.files) > 0:
+ for file in args.files:
+ csv_readers.append(dict_reader(open(file, 'r')))
+elif args.zip_input:
+ with ZipFile(args.zip_input) as zip:
+ for entry in zip.namelist():
+ if entry.endswith('.uau'):
+ csv_readers.append(dict_reader(io.TextIOWrapper(zip.open(entry, 'r'))))
+
headers = set()
-for reader in csv_readers:
- headers = headers.union(reader.fieldnames)
+if args.header:
+ fieldnames = args.header.split(',')
+else:
+ # Build union of all columns from source files:
+ for reader in csv_readers:
+ headers = headers.union(reader.fieldnames)
+ fieldnames = sorted(headers)
# Concatenate all files to output:
-out = csv.DictWriter(sys.stdout, delimiter=',', quotechar='|', quoting=csv.QUOTE_MINIMAL,
- dialect='unix', fieldnames=sorted(headers))
-out.writeheader()
+writer = csv.DictWriter(args.output, delimiter=',', quotechar='|', quoting=csv.QUOTE_MINIMAL,
+ dialect='unix', fieldnames=fieldnames)
+writer.writeheader()
for reader in csv_readers:
for row in reader:
- out.writerow(row)
+ writer.writerow(row)