Merge changes from topics "embms-cts-6", "embms-cts-5"
* changes:
Add @TestApi for MbmsDownloadReceiver CTS
Add @TestApi for DownloadStateCallback CTS
diff --git a/Android.mk b/Android.mk
index 2b12416..dd53793 100644
--- a/Android.mk
+++ b/Android.mk
@@ -138,6 +138,7 @@
../../system/bt/binder/android/bluetooth/IBluetoothPbap.aidl \
../../system/bt/binder/android/bluetooth/IBluetoothPbapClient.aidl \
../../system/bt/binder/android/bluetooth/IBluetoothSap.aidl \
+ ../../system/bt/binder/android/bluetooth/IBluetoothSocketManager.aidl \
../../system/bt/binder/android/bluetooth/IBluetoothStateChangeCallback.aidl \
../../system/bt/binder/android/bluetooth/IBluetoothHeadsetClient.aidl \
../../system/bt/binder/android/bluetooth/IBluetoothHidDevice.aidl \
@@ -906,8 +907,7 @@
# FRAMEWORKS_BASE_SUBDIRS comes from build/core/pathmap.mk
dirs_to_document := \
$(dirs_to_check_apis) \
- $(addprefix ../../, $(FRAMEWORKS_DATA_BINDING_JAVA_SRC_DIRS)) \
- $(addprefix ../../, $(FRAMEWORKS_SUPPORT_JAVA_SRC_DIRS)) \
+ $(addprefix ../../, $(FRAMEWORKS_DATA_BINDING_JAVA_SRC_DIRS))
patterns_to_not_document := \
$(call find-no-docs-pattern, $(dirs_to_document))
@@ -942,7 +942,8 @@
framework_docs_LOCAL_INTERMEDIATE_SOURCES := \
$(framework_res_source_path)/android/R.java \
$(framework_res_source_path)/android/Manifest.java \
- $(framework_res_source_path)/com/android/internal/R.java
+ $(framework_res_source_path)/com/android/internal/R.java \
+ $(patsubst $(TARGET_OUT_COMMON_INTERMEDIATES)/%,%,$(libcore_to_document_generated))
framework_docs_LOCAL_API_CHECK_JAVA_LIBRARIES := \
core-oj \
@@ -955,6 +956,8 @@
framework \
voip-common
+# Platform docs can refer to Support Library APIs, but we don't actually build
+# them as part of the docs target, so we need to include them on the classpath.
framework_docs_LOCAL_JAVA_LIBRARIES := \
$(framework_docs_LOCAL_API_CHECK_JAVA_LIBRARIES) \
$(FRAMEWORKS_SUPPORT_JAVA_LIBRARIES)
@@ -1001,20 +1004,15 @@
-werror -hide 111 -hide 113 -hide 121 \
-overview $(LOCAL_PATH)/core/java/overview.html \
-# Allow the support library to add its own droiddoc options.
-include $(LOCAL_PATH)/../support/droiddoc.mk
-
framework_docs_LOCAL_API_CHECK_ADDITIONAL_JAVA_DIR:= \
$(call intermediates-dir-for,JAVA_LIBRARIES,framework,,COMMON)
framework_docs_LOCAL_ADDITIONAL_JAVA_DIR:= \
- $(framework_docs_LOCAL_API_CHECK_ADDITIONAL_JAVA_DIR) \
- $(foreach lib,$(FRAMEWORKS_SUPPORT_JAVA_LIBRARIES),$(call intermediates-dir-for,JAVA_LIBRARIES,$(lib),,COMMON)) \
- $(foreach lib,$(FRAMEWORKS_SUPPORT_JAVA_LIBRARIES),$(call intermediates-dir-for,JAVA_LIBRARIES,$(lib)-res,,COMMON))
+ $(framework_docs_LOCAL_API_CHECK_ADDITIONAL_JAVA_DIR)
framework_docs_LOCAL_ADDITIONAL_DEPENDENCIES := \
frameworks/base/docs/knowntags.txt \
- libcore/Docs.mk
+ $(libcore_to_document_generated)
samples_dir := development/samples/browseable
@@ -1053,6 +1051,11 @@
-resourcesdir $(LOCAL_PATH)/docs/html/reference/images/ \
-resourcesoutdir reference/android/images/
+# Federate Support Library references against local API file.
+framework_docs_LOCAL_DROIDDOC_OPTIONS += \
+ -federate SupportLib https://developer.android.com \
+ -federationapi SupportLib prebuilts/sdk/current/support-api.txt
+
# ==== the api stubs and current.xml ===========================
include $(CLEAR_VARS)
diff --git a/api/current.txt b/api/current.txt
index eca65be..4dbbfaa 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -18040,13 +18040,11 @@
method public synchronized void applyLocalizedPattern(java.lang.String);
method public synchronized void applyPattern(java.lang.String);
method public synchronized boolean areSignificantDigitsUsed();
- method public synchronized boolean equals(java.lang.Object);
method public java.lang.StringBuffer format(double, java.lang.StringBuffer, java.text.FieldPosition);
method public java.lang.StringBuffer format(long, java.lang.StringBuffer, java.text.FieldPosition);
method public java.lang.StringBuffer format(java.math.BigInteger, java.lang.StringBuffer, java.text.FieldPosition);
method public java.lang.StringBuffer format(java.math.BigDecimal, java.lang.StringBuffer, java.text.FieldPosition);
method public java.lang.StringBuffer format(android.icu.math.BigDecimal, java.lang.StringBuffer, java.text.FieldPosition);
- method public synchronized android.icu.util.Currency getCurrency();
method public synchronized android.icu.text.CurrencyPluralInfo getCurrencyPluralInfo();
method public synchronized android.icu.util.Currency.CurrencyUsage getCurrencyUsage();
method public synchronized android.icu.text.DecimalFormatSymbols getDecimalFormatSymbols();
@@ -18054,12 +18052,8 @@
method public synchronized int getGroupingSize();
method public synchronized java.math.MathContext getMathContext();
method public synchronized android.icu.math.MathContext getMathContextICU();
- method public synchronized int getMaximumFractionDigits();
- method public synchronized int getMaximumIntegerDigits();
method public synchronized int getMaximumSignificantDigits();
method public synchronized byte getMinimumExponentDigits();
- method public synchronized int getMinimumFractionDigits();
- method public synchronized int getMinimumIntegerDigits();
method public synchronized int getMinimumSignificantDigits();
method public synchronized int getMultiplier();
method public synchronized java.lang.String getNegativePrefix();
@@ -18070,19 +18064,13 @@
method public synchronized java.lang.String getPositivePrefix();
method public synchronized java.lang.String getPositiveSuffix();
method public synchronized java.math.BigDecimal getRoundingIncrement();
- method public synchronized int getRoundingMode();
method public synchronized int getSecondaryGroupingSize();
- method public synchronized int hashCode();
method public synchronized boolean isDecimalPatternMatchRequired();
method public synchronized boolean isDecimalSeparatorAlwaysShown();
method public synchronized boolean isExponentSignAlwaysShown();
- method public synchronized boolean isGroupingUsed();
method public synchronized boolean isParseBigDecimal();
- method public synchronized boolean isParseIntegerOnly();
- method public synchronized boolean isParseStrict();
method public synchronized boolean isScientificNotation();
method public java.lang.Number parse(java.lang.String, java.text.ParsePosition);
- method public synchronized void setCurrency(android.icu.util.Currency);
method public synchronized void setCurrencyPluralInfo(android.icu.text.CurrencyPluralInfo);
method public synchronized void setCurrencyUsage(android.icu.util.Currency.CurrencyUsage);
method public synchronized void setDecimalFormatSymbols(android.icu.text.DecimalFormatSymbols);
@@ -18091,15 +18079,10 @@
method public synchronized void setExponentSignAlwaysShown(boolean);
method public synchronized void setFormatWidth(int);
method public synchronized void setGroupingSize(int);
- method public synchronized void setGroupingUsed(boolean);
method public synchronized void setMathContext(java.math.MathContext);
method public synchronized void setMathContextICU(android.icu.math.MathContext);
- method public synchronized void setMaximumFractionDigits(int);
- method public synchronized void setMaximumIntegerDigits(int);
method public synchronized void setMaximumSignificantDigits(int);
method public synchronized void setMinimumExponentDigits(byte);
- method public synchronized void setMinimumFractionDigits(int);
- method public synchronized void setMinimumIntegerDigits(int);
method public synchronized void setMinimumSignificantDigits(int);
method public synchronized void setMultiplier(int);
method public synchronized void setNegativePrefix(java.lang.String);
@@ -18107,15 +18090,12 @@
method public synchronized void setPadCharacter(char);
method public synchronized void setPadPosition(int);
method public synchronized void setParseBigDecimal(boolean);
- method public synchronized void setParseIntegerOnly(boolean);
method public deprecated void setParseMaxDigits(int);
- method public synchronized void setParseStrict(boolean);
method public synchronized void setPositivePrefix(java.lang.String);
method public synchronized void setPositiveSuffix(java.lang.String);
method public synchronized void setRoundingIncrement(java.math.BigDecimal);
method public synchronized void setRoundingIncrement(android.icu.math.BigDecimal);
method public synchronized void setRoundingIncrement(double);
- method public synchronized void setRoundingMode(int);
method public synchronized void setScientificNotation(boolean);
method public synchronized void setSecondaryGroupingSize(int);
method public synchronized void setSignificantDigitsUsed(boolean);
@@ -25557,7 +25537,6 @@
public static final class IpSecManager.SecurityParameterIndex implements java.lang.AutoCloseable {
method public void close();
- method protected void finalize();
method public int getSpi();
}
@@ -25712,6 +25691,7 @@
field public static final int NET_CAPABILITY_MMS = 0; // 0x0
field public static final int NET_CAPABILITY_NOT_METERED = 11; // 0xb
field public static final int NET_CAPABILITY_NOT_RESTRICTED = 13; // 0xd
+ field public static final int NET_CAPABILITY_NOT_ROAMING = 18; // 0x12
field public static final int NET_CAPABILITY_NOT_VPN = 15; // 0xf
field public static final int NET_CAPABILITY_RCS = 8; // 0x8
field public static final int NET_CAPABILITY_SUPL = 1; // 0x1
@@ -25742,7 +25722,7 @@
method public boolean isConnected();
method public boolean isConnectedOrConnecting();
method public boolean isFailover();
- method public boolean isRoaming();
+ method public deprecated boolean isRoaming();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.net.NetworkInfo> CREATOR;
}
@@ -30747,6 +30727,7 @@
}
public final class Debug {
+ method public static void attachJvmtiAgent(java.lang.String, java.lang.String) throws java.io.IOException;
method public static deprecated void changeDebugPort(int);
method public static void dumpHprofData(java.lang.String) throws java.io.IOException;
method public static boolean dumpService(java.lang.String, java.io.FileDescriptor, java.lang.String[]);
@@ -38024,6 +38005,16 @@
field public final int errno;
}
+ public class Int32Ref {
+ ctor public Int32Ref(int);
+ field public int value;
+ }
+
+ public class Int64Ref {
+ ctor public Int64Ref(long);
+ field public long value;
+ }
+
public final class Os {
method public static java.io.FileDescriptor accept(java.io.FileDescriptor, java.net.InetSocketAddress) throws android.system.ErrnoException, java.net.SocketException;
method public static boolean access(java.lang.String, int) throws android.system.ErrnoException;
@@ -38093,7 +38084,8 @@
method public static void remove(java.lang.String) throws android.system.ErrnoException;
method public static void removexattr(java.lang.String, java.lang.String) throws android.system.ErrnoException;
method public static void rename(java.lang.String, java.lang.String) throws android.system.ErrnoException;
- method public static long sendfile(java.io.FileDescriptor, java.io.FileDescriptor, android.util.MutableLong, long) throws android.system.ErrnoException;
+ method public static deprecated long sendfile(java.io.FileDescriptor, java.io.FileDescriptor, android.util.MutableLong, long) throws android.system.ErrnoException;
+ method public static long sendfile(java.io.FileDescriptor, java.io.FileDescriptor, android.system.Int64Ref, long) throws android.system.ErrnoException;
method public static int sendto(java.io.FileDescriptor, java.nio.ByteBuffer, int, java.net.InetAddress, int) throws android.system.ErrnoException, java.net.SocketException;
method public static int sendto(java.io.FileDescriptor, byte[], int, int, int, java.net.InetAddress, int) throws android.system.ErrnoException, java.net.SocketException;
method public static void setegid(int) throws android.system.ErrnoException;
@@ -38118,7 +38110,8 @@
method public static int umask(int);
method public static android.system.StructUtsname uname();
method public static void unsetenv(java.lang.String) throws android.system.ErrnoException;
- method public static int waitpid(int, android.util.MutableInt, int) throws android.system.ErrnoException;
+ method public static deprecated int waitpid(int, android.util.MutableInt, int) throws android.system.ErrnoException;
+ method public static int waitpid(int, android.system.Int32Ref, int) throws android.system.ErrnoException;
method public static int write(java.io.FileDescriptor, java.nio.ByteBuffer) throws android.system.ErrnoException, java.io.InterruptedIOException;
method public static int write(java.io.FileDescriptor, byte[], int, int) throws android.system.ErrnoException, java.io.InterruptedIOException;
method public static int writev(java.io.FileDescriptor, java.lang.Object[], int[], int[]) throws android.system.ErrnoException, java.io.InterruptedIOException;
@@ -38813,7 +38806,9 @@
ctor public CallAudioState(boolean, int, int);
method public static java.lang.String audioRouteToString(int);
method public int describeContents();
+ method public android.bluetooth.BluetoothDevice getActiveBluetoothDevice();
method public int getRoute();
+ method public java.util.Collection<android.bluetooth.BluetoothDevice> getSupportedBluetoothDevices();
method public int getSupportedRouteMask();
method public boolean isMuted();
method public void writeToParcel(android.os.Parcel, int);
@@ -38945,6 +38940,7 @@
method public final void putExtras(android.os.Bundle);
method public final void removeExtras(java.util.List<java.lang.String>);
method public final void removeExtras(java.lang.String...);
+ method public void requestBluetoothAudio(java.lang.String);
method public void sendConnectionEvent(java.lang.String, android.os.Bundle);
method public final void setActive();
method public final void setAddress(android.net.Uri, int);
@@ -39135,6 +39131,7 @@
method public void onCanAddCallChanged(boolean);
method public void onConnectionEvent(android.telecom.Call, java.lang.String, android.os.Bundle);
method public void onSilenceRinger();
+ method public final void requestBluetoothAudio(java.lang.String);
method public final void setAudioRoute(int);
method public final void setMuted(boolean);
field public static final java.lang.String SERVICE_INTERFACE = "android.telecom.InCallService";
@@ -39524,6 +39521,7 @@
field public static final java.lang.String KEY_DURATION_BLOCKING_DISABLED_AFTER_EMERGENCY_INT = "duration_blocking_disabled_after_emergency_int";
field public static final java.lang.String KEY_EDITABLE_ENHANCED_4G_LTE_BOOL = "editable_enhanced_4g_lte_bool";
field public static final java.lang.String KEY_EDITABLE_VOICEMAIL_NUMBER_BOOL = "editable_voicemail_number_bool";
+ field public static final java.lang.String KEY_EDITABLE_VOICEMAIL_NUMBER_SETTING_BOOL = "editable_voicemail_number_setting_bool";
field public static final java.lang.String KEY_ENABLE_DIALER_KEY_VIBRATION_BOOL = "enable_dialer_key_vibration_bool";
field public static final java.lang.String KEY_FORCE_HOME_NETWORK_BOOL = "force_home_network_bool";
field public static final java.lang.String KEY_GSM_DTMF_TONE_DELAY_INT = "gsm_dtmf_tone_delay_int";
diff --git a/api/system-current.txt b/api/system-current.txt
index 2e2cb4c..9bce265 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -19548,13 +19548,11 @@
method public synchronized void applyLocalizedPattern(java.lang.String);
method public synchronized void applyPattern(java.lang.String);
method public synchronized boolean areSignificantDigitsUsed();
- method public synchronized boolean equals(java.lang.Object);
method public java.lang.StringBuffer format(double, java.lang.StringBuffer, java.text.FieldPosition);
method public java.lang.StringBuffer format(long, java.lang.StringBuffer, java.text.FieldPosition);
method public java.lang.StringBuffer format(java.math.BigInteger, java.lang.StringBuffer, java.text.FieldPosition);
method public java.lang.StringBuffer format(java.math.BigDecimal, java.lang.StringBuffer, java.text.FieldPosition);
method public java.lang.StringBuffer format(android.icu.math.BigDecimal, java.lang.StringBuffer, java.text.FieldPosition);
- method public synchronized android.icu.util.Currency getCurrency();
method public synchronized android.icu.text.CurrencyPluralInfo getCurrencyPluralInfo();
method public synchronized android.icu.util.Currency.CurrencyUsage getCurrencyUsage();
method public synchronized android.icu.text.DecimalFormatSymbols getDecimalFormatSymbols();
@@ -19562,12 +19560,8 @@
method public synchronized int getGroupingSize();
method public synchronized java.math.MathContext getMathContext();
method public synchronized android.icu.math.MathContext getMathContextICU();
- method public synchronized int getMaximumFractionDigits();
- method public synchronized int getMaximumIntegerDigits();
method public synchronized int getMaximumSignificantDigits();
method public synchronized byte getMinimumExponentDigits();
- method public synchronized int getMinimumFractionDigits();
- method public synchronized int getMinimumIntegerDigits();
method public synchronized int getMinimumSignificantDigits();
method public synchronized int getMultiplier();
method public synchronized java.lang.String getNegativePrefix();
@@ -19578,19 +19572,13 @@
method public synchronized java.lang.String getPositivePrefix();
method public synchronized java.lang.String getPositiveSuffix();
method public synchronized java.math.BigDecimal getRoundingIncrement();
- method public synchronized int getRoundingMode();
method public synchronized int getSecondaryGroupingSize();
- method public synchronized int hashCode();
method public synchronized boolean isDecimalPatternMatchRequired();
method public synchronized boolean isDecimalSeparatorAlwaysShown();
method public synchronized boolean isExponentSignAlwaysShown();
- method public synchronized boolean isGroupingUsed();
method public synchronized boolean isParseBigDecimal();
- method public synchronized boolean isParseIntegerOnly();
- method public synchronized boolean isParseStrict();
method public synchronized boolean isScientificNotation();
method public java.lang.Number parse(java.lang.String, java.text.ParsePosition);
- method public synchronized void setCurrency(android.icu.util.Currency);
method public synchronized void setCurrencyPluralInfo(android.icu.text.CurrencyPluralInfo);
method public synchronized void setCurrencyUsage(android.icu.util.Currency.CurrencyUsage);
method public synchronized void setDecimalFormatSymbols(android.icu.text.DecimalFormatSymbols);
@@ -19599,15 +19587,10 @@
method public synchronized void setExponentSignAlwaysShown(boolean);
method public synchronized void setFormatWidth(int);
method public synchronized void setGroupingSize(int);
- method public synchronized void setGroupingUsed(boolean);
method public synchronized void setMathContext(java.math.MathContext);
method public synchronized void setMathContextICU(android.icu.math.MathContext);
- method public synchronized void setMaximumFractionDigits(int);
- method public synchronized void setMaximumIntegerDigits(int);
method public synchronized void setMaximumSignificantDigits(int);
method public synchronized void setMinimumExponentDigits(byte);
- method public synchronized void setMinimumFractionDigits(int);
- method public synchronized void setMinimumIntegerDigits(int);
method public synchronized void setMinimumSignificantDigits(int);
method public synchronized void setMultiplier(int);
method public synchronized void setNegativePrefix(java.lang.String);
@@ -19615,15 +19598,12 @@
method public synchronized void setPadCharacter(char);
method public synchronized void setPadPosition(int);
method public synchronized void setParseBigDecimal(boolean);
- method public synchronized void setParseIntegerOnly(boolean);
method public deprecated void setParseMaxDigits(int);
- method public synchronized void setParseStrict(boolean);
method public synchronized void setPositivePrefix(java.lang.String);
method public synchronized void setPositiveSuffix(java.lang.String);
method public synchronized void setRoundingIncrement(java.math.BigDecimal);
method public synchronized void setRoundingIncrement(android.icu.math.BigDecimal);
method public synchronized void setRoundingIncrement(double);
- method public synchronized void setRoundingMode(int);
method public synchronized void setScientificNotation(boolean);
method public synchronized void setSecondaryGroupingSize(int);
method public synchronized void setSignificantDigitsUsed(boolean);
@@ -27753,7 +27733,6 @@
public static final class IpSecManager.SecurityParameterIndex implements java.lang.AutoCloseable {
method public void close();
- method protected void finalize();
method public int getSpi();
}
@@ -27910,6 +27889,7 @@
field public static final int NET_CAPABILITY_MMS = 0; // 0x0
field public static final int NET_CAPABILITY_NOT_METERED = 11; // 0xb
field public static final int NET_CAPABILITY_NOT_RESTRICTED = 13; // 0xd
+ field public static final int NET_CAPABILITY_NOT_ROAMING = 18; // 0x12
field public static final int NET_CAPABILITY_NOT_VPN = 15; // 0xf
field public static final int NET_CAPABILITY_RCS = 8; // 0x8
field public static final int NET_CAPABILITY_SUPL = 1; // 0x1
@@ -27940,7 +27920,7 @@
method public boolean isConnected();
method public boolean isConnectedOrConnecting();
method public boolean isFailover();
- method public boolean isRoaming();
+ method public deprecated boolean isRoaming();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.net.NetworkInfo> CREATOR;
}
@@ -33470,6 +33450,7 @@
}
public final class Debug {
+ method public static void attachJvmtiAgent(java.lang.String, java.lang.String) throws java.io.IOException;
method public static deprecated void changeDebugPort(int);
method public static void dumpHprofData(java.lang.String) throws java.io.IOException;
method public static boolean dumpService(java.lang.String, java.io.FileDescriptor, java.lang.String[]);
@@ -41238,6 +41219,16 @@
field public final int errno;
}
+ public class Int32Ref {
+ ctor public Int32Ref(int);
+ field public int value;
+ }
+
+ public class Int64Ref {
+ ctor public Int64Ref(long);
+ field public long value;
+ }
+
public final class Os {
method public static java.io.FileDescriptor accept(java.io.FileDescriptor, java.net.InetSocketAddress) throws android.system.ErrnoException, java.net.SocketException;
method public static boolean access(java.lang.String, int) throws android.system.ErrnoException;
@@ -41307,7 +41298,8 @@
method public static void remove(java.lang.String) throws android.system.ErrnoException;
method public static void removexattr(java.lang.String, java.lang.String) throws android.system.ErrnoException;
method public static void rename(java.lang.String, java.lang.String) throws android.system.ErrnoException;
- method public static long sendfile(java.io.FileDescriptor, java.io.FileDescriptor, android.util.MutableLong, long) throws android.system.ErrnoException;
+ method public static deprecated long sendfile(java.io.FileDescriptor, java.io.FileDescriptor, android.util.MutableLong, long) throws android.system.ErrnoException;
+ method public static long sendfile(java.io.FileDescriptor, java.io.FileDescriptor, android.system.Int64Ref, long) throws android.system.ErrnoException;
method public static int sendto(java.io.FileDescriptor, java.nio.ByteBuffer, int, java.net.InetAddress, int) throws android.system.ErrnoException, java.net.SocketException;
method public static int sendto(java.io.FileDescriptor, byte[], int, int, int, java.net.InetAddress, int) throws android.system.ErrnoException, java.net.SocketException;
method public static void setegid(int) throws android.system.ErrnoException;
@@ -41332,7 +41324,8 @@
method public static int umask(int);
method public static android.system.StructUtsname uname();
method public static void unsetenv(java.lang.String) throws android.system.ErrnoException;
- method public static int waitpid(int, android.util.MutableInt, int) throws android.system.ErrnoException;
+ method public static deprecated int waitpid(int, android.util.MutableInt, int) throws android.system.ErrnoException;
+ method public static int waitpid(int, android.system.Int32Ref, int) throws android.system.ErrnoException;
method public static int write(java.io.FileDescriptor, java.nio.ByteBuffer) throws android.system.ErrnoException, java.io.InterruptedIOException;
method public static int write(java.io.FileDescriptor, byte[], int, int) throws android.system.ErrnoException, java.io.InterruptedIOException;
method public static int writev(java.io.FileDescriptor, java.lang.Object[], int[], int[]) throws android.system.ErrnoException, java.io.InterruptedIOException;
@@ -42052,7 +42045,9 @@
ctor public CallAudioState(boolean, int, int);
method public static java.lang.String audioRouteToString(int);
method public int describeContents();
+ method public android.bluetooth.BluetoothDevice getActiveBluetoothDevice();
method public int getRoute();
+ method public java.util.Collection<android.bluetooth.BluetoothDevice> getSupportedBluetoothDevices();
method public int getSupportedRouteMask();
method public boolean isMuted();
method public void writeToParcel(android.os.Parcel, int);
@@ -42191,6 +42186,7 @@
method public final void putExtras(android.os.Bundle);
method public final void removeExtras(java.util.List<java.lang.String>);
method public final void removeExtras(java.lang.String...);
+ method public void requestBluetoothAudio(java.lang.String);
method public void sendConnectionEvent(java.lang.String, android.os.Bundle);
method public final void setActive();
method public final void setAddress(android.net.Uri, int);
@@ -42384,6 +42380,7 @@
method public deprecated void onPhoneCreated(android.telecom.Phone);
method public deprecated void onPhoneDestroyed(android.telecom.Phone);
method public void onSilenceRinger();
+ method public final void requestBluetoothAudio(java.lang.String);
method public final void setAudioRoute(int);
method public final void setMuted(boolean);
field public static final java.lang.String SERVICE_INTERFACE = "android.telecom.InCallService";
@@ -42522,6 +42519,7 @@
method public final android.telecom.CallAudioState getCallAudioState();
method public final java.util.List<android.telecom.Call> getCalls();
method public final void removeListener(android.telecom.Phone.Listener);
+ method public void requestBluetoothAudio(java.lang.String);
method public final void setAudioRoute(int);
method public final void setMuted(boolean);
}
@@ -42955,6 +42953,7 @@
field public static final java.lang.String KEY_DURATION_BLOCKING_DISABLED_AFTER_EMERGENCY_INT = "duration_blocking_disabled_after_emergency_int";
field public static final java.lang.String KEY_EDITABLE_ENHANCED_4G_LTE_BOOL = "editable_enhanced_4g_lte_bool";
field public static final java.lang.String KEY_EDITABLE_VOICEMAIL_NUMBER_BOOL = "editable_voicemail_number_bool";
+ field public static final java.lang.String KEY_EDITABLE_VOICEMAIL_NUMBER_SETTING_BOOL = "editable_voicemail_number_setting_bool";
field public static final java.lang.String KEY_ENABLE_DIALER_KEY_VIBRATION_BOOL = "enable_dialer_key_vibration_bool";
field public static final java.lang.String KEY_FORCE_HOME_NETWORK_BOOL = "force_home_network_bool";
field public static final java.lang.String KEY_GSM_DTMF_TONE_DELAY_INT = "gsm_dtmf_tone_delay_int";
diff --git a/api/test-current.txt b/api/test-current.txt
index 05bcab6..7534e9f 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -18092,13 +18092,11 @@
method public synchronized void applyLocalizedPattern(java.lang.String);
method public synchronized void applyPattern(java.lang.String);
method public synchronized boolean areSignificantDigitsUsed();
- method public synchronized boolean equals(java.lang.Object);
method public java.lang.StringBuffer format(double, java.lang.StringBuffer, java.text.FieldPosition);
method public java.lang.StringBuffer format(long, java.lang.StringBuffer, java.text.FieldPosition);
method public java.lang.StringBuffer format(java.math.BigInteger, java.lang.StringBuffer, java.text.FieldPosition);
method public java.lang.StringBuffer format(java.math.BigDecimal, java.lang.StringBuffer, java.text.FieldPosition);
method public java.lang.StringBuffer format(android.icu.math.BigDecimal, java.lang.StringBuffer, java.text.FieldPosition);
- method public synchronized android.icu.util.Currency getCurrency();
method public synchronized android.icu.text.CurrencyPluralInfo getCurrencyPluralInfo();
method public synchronized android.icu.util.Currency.CurrencyUsage getCurrencyUsage();
method public synchronized android.icu.text.DecimalFormatSymbols getDecimalFormatSymbols();
@@ -18106,12 +18104,8 @@
method public synchronized int getGroupingSize();
method public synchronized java.math.MathContext getMathContext();
method public synchronized android.icu.math.MathContext getMathContextICU();
- method public synchronized int getMaximumFractionDigits();
- method public synchronized int getMaximumIntegerDigits();
method public synchronized int getMaximumSignificantDigits();
method public synchronized byte getMinimumExponentDigits();
- method public synchronized int getMinimumFractionDigits();
- method public synchronized int getMinimumIntegerDigits();
method public synchronized int getMinimumSignificantDigits();
method public synchronized int getMultiplier();
method public synchronized java.lang.String getNegativePrefix();
@@ -18122,19 +18116,13 @@
method public synchronized java.lang.String getPositivePrefix();
method public synchronized java.lang.String getPositiveSuffix();
method public synchronized java.math.BigDecimal getRoundingIncrement();
- method public synchronized int getRoundingMode();
method public synchronized int getSecondaryGroupingSize();
- method public synchronized int hashCode();
method public synchronized boolean isDecimalPatternMatchRequired();
method public synchronized boolean isDecimalSeparatorAlwaysShown();
method public synchronized boolean isExponentSignAlwaysShown();
- method public synchronized boolean isGroupingUsed();
method public synchronized boolean isParseBigDecimal();
- method public synchronized boolean isParseIntegerOnly();
- method public synchronized boolean isParseStrict();
method public synchronized boolean isScientificNotation();
method public java.lang.Number parse(java.lang.String, java.text.ParsePosition);
- method public synchronized void setCurrency(android.icu.util.Currency);
method public synchronized void setCurrencyPluralInfo(android.icu.text.CurrencyPluralInfo);
method public synchronized void setCurrencyUsage(android.icu.util.Currency.CurrencyUsage);
method public synchronized void setDecimalFormatSymbols(android.icu.text.DecimalFormatSymbols);
@@ -18143,15 +18131,10 @@
method public synchronized void setExponentSignAlwaysShown(boolean);
method public synchronized void setFormatWidth(int);
method public synchronized void setGroupingSize(int);
- method public synchronized void setGroupingUsed(boolean);
method public synchronized void setMathContext(java.math.MathContext);
method public synchronized void setMathContextICU(android.icu.math.MathContext);
- method public synchronized void setMaximumFractionDigits(int);
- method public synchronized void setMaximumIntegerDigits(int);
method public synchronized void setMaximumSignificantDigits(int);
method public synchronized void setMinimumExponentDigits(byte);
- method public synchronized void setMinimumFractionDigits(int);
- method public synchronized void setMinimumIntegerDigits(int);
method public synchronized void setMinimumSignificantDigits(int);
method public synchronized void setMultiplier(int);
method public synchronized void setNegativePrefix(java.lang.String);
@@ -18159,15 +18142,12 @@
method public synchronized void setPadCharacter(char);
method public synchronized void setPadPosition(int);
method public synchronized void setParseBigDecimal(boolean);
- method public synchronized void setParseIntegerOnly(boolean);
method public deprecated void setParseMaxDigits(int);
- method public synchronized void setParseStrict(boolean);
method public synchronized void setPositivePrefix(java.lang.String);
method public synchronized void setPositiveSuffix(java.lang.String);
method public synchronized void setRoundingIncrement(java.math.BigDecimal);
method public synchronized void setRoundingIncrement(android.icu.math.BigDecimal);
method public synchronized void setRoundingIncrement(double);
- method public synchronized void setRoundingMode(int);
method public synchronized void setScientificNotation(boolean);
method public synchronized void setSecondaryGroupingSize(int);
method public synchronized void setSignificantDigitsUsed(boolean);
@@ -25667,7 +25647,6 @@
public static final class IpSecManager.SecurityParameterIndex implements java.lang.AutoCloseable {
method public void close();
- method protected void finalize();
method public int getSpi();
}
@@ -25822,6 +25801,7 @@
field public static final int NET_CAPABILITY_MMS = 0; // 0x0
field public static final int NET_CAPABILITY_NOT_METERED = 11; // 0xb
field public static final int NET_CAPABILITY_NOT_RESTRICTED = 13; // 0xd
+ field public static final int NET_CAPABILITY_NOT_ROAMING = 18; // 0x12
field public static final int NET_CAPABILITY_NOT_VPN = 15; // 0xf
field public static final int NET_CAPABILITY_RCS = 8; // 0x8
field public static final int NET_CAPABILITY_SUPL = 1; // 0x1
@@ -25852,7 +25832,7 @@
method public boolean isConnected();
method public boolean isConnectedOrConnecting();
method public boolean isFailover();
- method public boolean isRoaming();
+ method public deprecated boolean isRoaming();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.os.Parcelable.Creator<android.net.NetworkInfo> CREATOR;
}
@@ -30857,6 +30837,7 @@
}
public final class Debug {
+ method public static void attachJvmtiAgent(java.lang.String, java.lang.String) throws java.io.IOException;
method public static deprecated void changeDebugPort(int);
method public static void dumpHprofData(java.lang.String) throws java.io.IOException;
method public static boolean dumpService(java.lang.String, java.io.FileDescriptor, java.lang.String[]);
@@ -38231,6 +38212,16 @@
field public final int errno;
}
+ public class Int32Ref {
+ ctor public Int32Ref(int);
+ field public int value;
+ }
+
+ public class Int64Ref {
+ ctor public Int64Ref(long);
+ field public long value;
+ }
+
public final class Os {
method public static java.io.FileDescriptor accept(java.io.FileDescriptor, java.net.InetSocketAddress) throws android.system.ErrnoException, java.net.SocketException;
method public static boolean access(java.lang.String, int) throws android.system.ErrnoException;
@@ -38300,7 +38291,8 @@
method public static void remove(java.lang.String) throws android.system.ErrnoException;
method public static void removexattr(java.lang.String, java.lang.String) throws android.system.ErrnoException;
method public static void rename(java.lang.String, java.lang.String) throws android.system.ErrnoException;
- method public static long sendfile(java.io.FileDescriptor, java.io.FileDescriptor, android.util.MutableLong, long) throws android.system.ErrnoException;
+ method public static deprecated long sendfile(java.io.FileDescriptor, java.io.FileDescriptor, android.util.MutableLong, long) throws android.system.ErrnoException;
+ method public static long sendfile(java.io.FileDescriptor, java.io.FileDescriptor, android.system.Int64Ref, long) throws android.system.ErrnoException;
method public static int sendto(java.io.FileDescriptor, java.nio.ByteBuffer, int, java.net.InetAddress, int) throws android.system.ErrnoException, java.net.SocketException;
method public static int sendto(java.io.FileDescriptor, byte[], int, int, int, java.net.InetAddress, int) throws android.system.ErrnoException, java.net.SocketException;
method public static void setegid(int) throws android.system.ErrnoException;
@@ -38325,7 +38317,8 @@
method public static int umask(int);
method public static android.system.StructUtsname uname();
method public static void unsetenv(java.lang.String) throws android.system.ErrnoException;
- method public static int waitpid(int, android.util.MutableInt, int) throws android.system.ErrnoException;
+ method public static deprecated int waitpid(int, android.util.MutableInt, int) throws android.system.ErrnoException;
+ method public static int waitpid(int, android.system.Int32Ref, int) throws android.system.ErrnoException;
method public static int write(java.io.FileDescriptor, java.nio.ByteBuffer) throws android.system.ErrnoException, java.io.InterruptedIOException;
method public static int write(java.io.FileDescriptor, byte[], int, int) throws android.system.ErrnoException, java.io.InterruptedIOException;
method public static int writev(java.io.FileDescriptor, java.lang.Object[], int[], int[]) throws android.system.ErrnoException, java.io.InterruptedIOException;
@@ -39021,7 +39014,9 @@
ctor public CallAudioState(boolean, int, int);
method public static java.lang.String audioRouteToString(int);
method public int describeContents();
+ method public android.bluetooth.BluetoothDevice getActiveBluetoothDevice();
method public int getRoute();
+ method public java.util.Collection<android.bluetooth.BluetoothDevice> getSupportedBluetoothDevices();
method public int getSupportedRouteMask();
method public boolean isMuted();
method public void writeToParcel(android.os.Parcel, int);
@@ -39156,6 +39151,7 @@
method public final void putExtras(android.os.Bundle);
method public final void removeExtras(java.util.List<java.lang.String>);
method public final void removeExtras(java.lang.String...);
+ method public void requestBluetoothAudio(java.lang.String);
method public void sendConnectionEvent(java.lang.String, android.os.Bundle);
method public final void sendRemoteRttRequest();
method public final void sendRttInitiationFailure(int);
@@ -39359,6 +39355,7 @@
method public void onCanAddCallChanged(boolean);
method public void onConnectionEvent(android.telecom.Call, java.lang.String, android.os.Bundle);
method public void onSilenceRinger();
+ method public final void requestBluetoothAudio(java.lang.String);
method public final void setAudioRoute(int);
method public final void setMuted(boolean);
field public static final java.lang.String SERVICE_INTERFACE = "android.telecom.InCallService";
@@ -39748,6 +39745,7 @@
field public static final java.lang.String KEY_DURATION_BLOCKING_DISABLED_AFTER_EMERGENCY_INT = "duration_blocking_disabled_after_emergency_int";
field public static final java.lang.String KEY_EDITABLE_ENHANCED_4G_LTE_BOOL = "editable_enhanced_4g_lte_bool";
field public static final java.lang.String KEY_EDITABLE_VOICEMAIL_NUMBER_BOOL = "editable_voicemail_number_bool";
+ field public static final java.lang.String KEY_EDITABLE_VOICEMAIL_NUMBER_SETTING_BOOL = "editable_voicemail_number_setting_bool";
field public static final java.lang.String KEY_ENABLE_DIALER_KEY_VIBRATION_BOOL = "enable_dialer_key_vibration_bool";
field public static final java.lang.String KEY_FORCE_HOME_NETWORK_BOOL = "force_home_network_bool";
field public static final java.lang.String KEY_GSM_DTMF_TONE_DELAY_INT = "gsm_dtmf_tone_delay_int";
diff --git a/cmds/appwidget/appwidget b/cmds/appwidget/appwidget
index 6105009..26ab173 100755
--- a/cmds/appwidget/appwidget
+++ b/cmds/appwidget/appwidget
@@ -1,3 +1,4 @@
+#!/system/bin/sh
# Script to start "appwidget" on the device, which has a very rudimentary shell.
base=/system
export CLASSPATH=$base/framework/appwidget.jar
diff --git a/cmds/bmgr/bmgr b/cmds/bmgr/bmgr
index 6b4bbe2d..60b5833 100755
--- a/cmds/bmgr/bmgr
+++ b/cmds/bmgr/bmgr
@@ -1,3 +1,4 @@
+#!/system/bin/sh
# Script to start "bmgr" on the device, which has a very rudimentary
# shell.
#
diff --git a/cmds/bu/bu b/cmds/bu/bu
index e8dbc31..e50b53d 100755
--- a/cmds/bu/bu
+++ b/cmds/bu/bu
@@ -1,3 +1,4 @@
+#!/system/bin/sh
# Script to start "bu" on the device
#
base=/system
diff --git a/cmds/content/content b/cmds/content/content
index a8e056d..f1bfe17 100755
--- a/cmds/content/content
+++ b/cmds/content/content
@@ -1,3 +1,4 @@
+#!/system/bin/sh
# Script to start "content" on the device, which has a very rudimentary shell.
base=/system
export CLASSPATH=$base/framework/content.jar
diff --git a/cmds/dpm/dpm b/cmds/dpm/dpm
index c2e5cbb..e0efdc1 100755
--- a/cmds/dpm/dpm
+++ b/cmds/dpm/dpm
@@ -1,3 +1,4 @@
+#!/system/bin/sh
# Script to start "dpm" on the device
#
base=/system
diff --git a/cmds/ime/ime b/cmds/ime/ime
index 96c56d3..1a1fdd9 100755
--- a/cmds/ime/ime
+++ b/cmds/ime/ime
@@ -1,3 +1,4 @@
+#!/system/bin/sh
# Script to start "pm" on the device, which has a very rudimentary
# shell.
#
diff --git a/cmds/input/input b/cmds/input/input
index 7f1a18e..54ab947 100755
--- a/cmds/input/input
+++ b/cmds/input/input
@@ -1,3 +1,4 @@
+#!/system/bin/sh
# Script to start "input" on the device, which has a very rudimentary
# shell.
#
diff --git a/cmds/locksettings/locksettings b/cmds/locksettings/locksettings
index c963b23..0ef4fa9 100755
--- a/cmds/locksettings/locksettings
+++ b/cmds/locksettings/locksettings
@@ -1,3 +1,4 @@
+#!/system/bin/sh
# Script to start "locksettings" on the device
#
base=/system
diff --git a/cmds/media/media b/cmds/media/media
index 1194442..5c0eb2f 100755
--- a/cmds/media/media
+++ b/cmds/media/media
@@ -1,3 +1,4 @@
+#!/system/bin/sh
# Script to start "media_cmd" on the device, which has a very rudimentary
# shell.
#
diff --git a/cmds/pm/pm b/cmds/pm/pm
index 8183838..53f85b2 100755
--- a/cmds/pm/pm
+++ b/cmds/pm/pm
@@ -1,3 +1,4 @@
+#!/system/bin/sh
# Script to start "pm" on the device, which has a very rudimentary
# shell.
#
diff --git a/cmds/requestsync/requestsync b/cmds/requestsync/requestsync
index 9315675..2d5d0e4 100755
--- a/cmds/requestsync/requestsync
+++ b/cmds/requestsync/requestsync
@@ -1,3 +1,4 @@
+#!/system/bin/sh
# Script to start "requestsync" on the device
#
base=/system
diff --git a/cmds/sm/sm b/cmds/sm/sm
index 8fba007..4bc859e0 100755
--- a/cmds/sm/sm
+++ b/cmds/sm/sm
@@ -1,3 +1,4 @@
+#!/system/bin/sh
# Script to start "sm" on the device, which has a very rudimentary
# shell.
#
diff --git a/cmds/svc/svc b/cmds/svc/svc
index 27111cd..07b50fe 100755
--- a/cmds/svc/svc
+++ b/cmds/svc/svc
@@ -1,3 +1,4 @@
+#!/system/bin/sh
# Script to start "am" on the device, which has a very rudimentary
# shell.
#
diff --git a/cmds/telecom/telecom b/cmds/telecom/telecom
index 9efdcfd..a19036b 100755
--- a/cmds/telecom/telecom
+++ b/cmds/telecom/telecom
@@ -1,3 +1,4 @@
+#!/system/bin/sh
# Script to start "telecom" on the device
#
base=/system
diff --git a/cmds/uiautomator/cmds/uiautomator/uiautomator b/cmds/uiautomator/cmds/uiautomator/uiautomator
index 86a1dba..889c2b5 100755
--- a/cmds/uiautomator/cmds/uiautomator/uiautomator
+++ b/cmds/uiautomator/cmds/uiautomator/uiautomator
@@ -1,3 +1,4 @@
+#!/system/bin/sh
#
# Copyright (C) 2012 The Android Open Source Project
#
diff --git a/cmds/vr/vr b/cmds/vr/vr
index a279007..dbde02a 100755
--- a/cmds/vr/vr
+++ b/cmds/vr/vr
@@ -1,3 +1,4 @@
+#!/system/bin/sh
# Script to start "vr" on the device
#
base=/system
diff --git a/cmds/wm/wm b/cmds/wm/wm
index f7a5bc7..16d6bd6 100755
--- a/cmds/wm/wm
+++ b/cmds/wm/wm
@@ -1,3 +1,4 @@
+#!/system/bin/sh
# Script to start "wm" on the device, which has a very rudimentary
# shell.
#
diff --git a/config/compiled-classes-phone b/config/compiled-classes-phone
index 8c79809..17156e8 100644
--- a/config/compiled-classes-phone
+++ b/config/compiled-classes-phone
@@ -3948,9 +3948,6 @@
android.telephony.VoLteServiceState
android.telephony.VoLteServiceState$1
android.telephony.gsm.GsmCellLocation
-android.telephony.ims.ImsServiceProxy$INotifyStatusChanged
-android.telephony.ims.ImsServiceProxyCompat
-android.telephony.ims.feature.IMMTelFeature
android.telephony.ims.stub.ImsConfigImplBase
android.telephony.ims.stub.ImsEcbmImplBase
android.telephony.ims.stub.ImsUtImplBase
diff --git a/core/java/android/app/DexLoadReporter.java b/core/java/android/app/DexLoadReporter.java
index 5f61e07..d15dd6d 100644
--- a/core/java/android/app/DexLoadReporter.java
+++ b/core/java/android/app/DexLoadReporter.java
@@ -19,6 +19,7 @@
import android.os.FileUtils;
import android.os.RemoteException;
import android.os.SystemProperties;
+import android.system.ErrnoException;
import android.util.Slog;
import com.android.internal.annotations.GuardedBy;
@@ -26,10 +27,11 @@
import dalvik.system.BaseDexClassLoader;
import dalvik.system.VMRuntime;
+import libcore.io.Libcore;
+
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
@@ -114,13 +116,18 @@
registerSecondaryDexForProfiling(dexPathsForRegistration);
}
- private void notifyPackageManager(List<BaseDexClassLoader> ignored,
+ private void notifyPackageManager(List<BaseDexClassLoader> classLoadersChain,
List<String> classPaths) {
+ // Get the class loader names for the binder call.
+ List<String> classLoadersNames = new ArrayList<>(classPaths.size());
+ for (BaseDexClassLoader bdc : classLoadersChain) {
+ classLoadersNames.add(bdc.getClass().getName());
+ }
String packageName = ActivityThread.currentPackageName();
try {
// Notify only the paths of the first class loader for now.
ActivityThread.getPackageManager().notifyDexLoad(
- packageName, Arrays.asList(classPaths.get(0).split(File.pathSeparator)),
+ packageName, classLoadersNames, classPaths,
VMRuntime.getRuntime().vmInstructionSet());
} catch (RemoteException re) {
Slog.e(TAG, "Failed to notify PM about dex load for package " + packageName, re);
@@ -148,22 +155,50 @@
// The dex path is not a secondary dex file. Nothing to do.
return;
}
- File secondaryProfile = getSecondaryProfileFile(dexPath);
+
+ File realDexPath;
try {
- // Create the profile if not already there.
- // Returns true if the file was created, false if the file already exists.
- // or throws exceptions in case of errors.
+ // Secondary dex profiles are stored in the oat directory, next to the real dex file
+ // and have the same name with 'cur.prof' appended. We use the realpath because that
+ // is what installd is using when processing the dex file.
+ // NOTE: Keep in sync with installd.
+ realDexPath = new File(Libcore.os.realpath(dexPath));
+ } catch (ErrnoException ex) {
+ Slog.e(TAG, "Failed to get the real path of secondary dex " + dexPath
+ + ":" + ex.getMessage());
+ // Do not continue with registration if we could not retrieve the real path.
+ return;
+ }
+
+ // NOTE: Keep this in sync with installd expectations.
+ File secondaryProfileDir = new File(realDexPath.getParent(), "oat");
+ File secondaryProfile = new File(secondaryProfileDir, realDexPath.getName() + ".cur.prof");
+
+ // Create the profile if not already there.
+ // Returns true if the file was created, false if the file already exists.
+ // or throws exceptions in case of errors.
+ if (!secondaryProfileDir.exists()) {
+ if (!secondaryProfileDir.mkdir()) {
+ Slog.e(TAG, "Could not create the profile directory: " + secondaryProfile);
+ // Do not continue with registration if we could not create the oat dir.
+ return;
+ }
+ }
+
+ try {
boolean created = secondaryProfile.createNewFile();
if (DEBUG && created) {
Slog.i(TAG, "Created profile for secondary dex: " + secondaryProfile);
}
} catch (IOException ex) {
- Slog.e(TAG, "Failed to create profile for secondary dex " + secondaryProfile +
- ":" + ex.getMessage());
- // Don't move forward with the registration if we failed to create the profile.
+ Slog.e(TAG, "Failed to create profile for secondary dex " + dexPath
+ + ":" + ex.getMessage());
+ // Do not continue with registration if we could not create the profile files.
return;
}
+ // If we got here, the dex paths is a secondary dex and we were able to create the profile.
+ // Register the path to the runtime.
VMRuntime.registerAppInfo(secondaryProfile.getPath(), new String[] { dexPath });
}
@@ -177,11 +212,4 @@
}
return false;
}
-
- // Secondary dex profiles are stored next to the dex file and have the same
- // name with '.prof' appended.
- // NOTE: Keep in sync with installd.
- private File getSecondaryProfileFile(String dexPath) {
- return new File(dexPath + ".prof");
- }
}
diff --git a/core/java/android/app/usage/ExternalStorageStats.java b/core/java/android/app/usage/ExternalStorageStats.java
index d7e570f..f00e5c2 100644
--- a/core/java/android/app/usage/ExternalStorageStats.java
+++ b/core/java/android/app/usage/ExternalStorageStats.java
@@ -33,6 +33,7 @@
/** {@hide} */ public long videoBytes;
/** {@hide} */ public long imageBytes;
/** {@hide} */ public long appBytes;
+ /** {@hide} */ public long obbBytes;
/**
* Return the total bytes used by all files in the shared/external storage
@@ -97,6 +98,11 @@
}
/** {@hide} */
+ public @BytesLong long getObbBytes() {
+ return obbBytes;
+ }
+
+ /** {@hide} */
public ExternalStorageStats() {
}
@@ -107,6 +113,7 @@
this.videoBytes = in.readLong();
this.imageBytes = in.readLong();
this.appBytes = in.readLong();
+ this.obbBytes = in.readLong();
}
@Override
@@ -121,6 +128,7 @@
dest.writeLong(videoBytes);
dest.writeLong(imageBytes);
dest.writeLong(appBytes);
+ dest.writeLong(obbBytes);
}
public static final Creator<ExternalStorageStats> CREATOR = new Creator<ExternalStorageStats>() {
diff --git a/core/java/android/bluetooth/BluetoothSocket.java b/core/java/android/bluetooth/BluetoothSocket.java
index 4035ee1..23956e1 100644
--- a/core/java/android/bluetooth/BluetoothSocket.java
+++ b/core/java/android/bluetooth/BluetoothSocket.java
@@ -417,7 +417,7 @@
return -1;
}
try {
- mPfd = bluetoothProxy.createSocketChannel(mType, mServiceName,
+ mPfd = bluetoothProxy.getSocketManager().createSocketChannel(mType, mServiceName,
mUuid, mPort, getSecurityFlags());
} catch (RemoteException e) {
Log.e(TAG, Log.getStackTraceString(new Throwable()));
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index 06f7916..70af021 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -688,6 +688,10 @@
* Cycles do not exist because they are illegal and screened for during installation.
*
* May be null if no splits are installed, or if no dependencies exist between them.
+ *
+ * NOTE: Any change to the way split dependencies are stored must update the logic that
+ * creates the class loader context for dexopt (DexoptUtils#getClassLoaderContexts).
+ *
* @hide
*/
public SparseArray<int[]> splitDependencies;
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index 4b44a17..c9afd6b 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -469,11 +469,19 @@
* 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 dexPats the list of the dex files paths that have been loaded
+ * @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}.
+ * The dex files found in the first class path will be recorded in the usage file.
* @param loaderIsa the ISA of the loader process
*/
- oneway void notifyDexLoad(String loadingPackageName, in List<String> dexPaths,
- String loaderIsa);
+ oneway void notifyDexLoad(String loadingPackageName, in List<String> classLoadersNames,
+ in List<String> classPaths, String loaderIsa);
/**
* Register an application dex module with the package manager.
@@ -508,21 +516,13 @@
in boolean isSharedModule, IDexModuleRegisterCallback callback);
/**
- * Ask the package manager to perform a dex-opt for the given reason. The package
- * manager will map the reason to a compiler filter according to the current system
- * configuration.
- */
- boolean performDexOpt(String packageName, boolean checkProfiles,
- int compileReason, boolean force, boolean bootComplete);
-
- /**
* Ask the package manager to perform a dex-opt with the given compiler filter.
*
* Note: exposed only for the shell command to allow moving packages explicitly to a
* definite state.
*/
boolean performDexOptMode(String packageName, boolean checkProfiles,
- String targetCompilerFilter, boolean force, boolean bootComplete);
+ String targetCompilerFilter, boolean force, boolean bootComplete, String splitName);
/**
* Ask the package manager to perform a dex-opt with the given compiler filter on the
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index 0ccebc1c..6bf1425 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -619,6 +619,35 @@
*/
public static final int NETID_UNSET = 0;
+ /**
+ * Private DNS Mode values.
+ *
+ * The "private_dns_mode" global setting stores a String value which is
+ * expected to be one of the following.
+ */
+
+ /**
+ * @hide
+ */
+ public static final String PRIVATE_DNS_MODE_OFF = "off";
+ /**
+ * @hide
+ */
+ public static final String PRIVATE_DNS_MODE_OPPORTUNISTIC = "opportunistic";
+ /**
+ * @hide
+ */
+ public static final String PRIVATE_DNS_MODE_PROVIDER_HOSTNAME = "hostname";
+ /**
+ * The default Private DNS mode.
+ *
+ * This may change from release to release or may become dependent upon
+ * the capabilities of the underlying platform.
+ *
+ * @hide
+ */
+ public static final String PRIVATE_DNS_DEFAULT_MODE = PRIVATE_DNS_MODE_OPPORTUNISTIC;
+
private final IConnectivityManager mService;
/**
* A kludge to facilitate static access where a Context pointer isn't available, like in the
diff --git a/core/java/android/net/ConnectivityMetricsEvent.java b/core/java/android/net/ConnectivityMetricsEvent.java
index 46bb346..394ac42 100644
--- a/core/java/android/net/ConnectivityMetricsEvent.java
+++ b/core/java/android/net/ConnectivityMetricsEvent.java
@@ -18,6 +18,7 @@
import android.os.Parcel;
import android.os.Parcelable;
+
import com.android.internal.util.BitUtils;
/**
@@ -80,7 +81,7 @@
StringBuilder buffer = new StringBuilder("ConnectivityMetricsEvent(");
buffer.append(String.format("%tT.%tL", timestamp, timestamp));
if (netId != 0) {
- buffer.append(", ").append(netId);
+ buffer.append(", ").append("netId=").append(netId);
}
if (ifname != null) {
buffer.append(", ").append(ifname);
diff --git a/core/java/android/net/IpSecAlgorithm.java b/core/java/android/net/IpSecAlgorithm.java
index 16b1452..64f8f39 100644
--- a/core/java/android/net/IpSecAlgorithm.java
+++ b/core/java/android/net/IpSecAlgorithm.java
@@ -78,7 +78,11 @@
/**
* AES-GCM Authentication/Integrity + Encryption/Ciphering Algorithm.
*
- * <p>Valid lengths for this key are {128, 192, 256}.
+ * <p>Valid lengths for keying material are {160, 224, 288}.
+ *
+ * <p>As per RFC4106 (Section 8.1), keying material consists of a 128, 192, or 256 bit AES key
+ * followed by a 32-bit salt. RFC compliance requires that the salt must be unique per
+ * invocation with the same key.
*
* <p>Valid ICV (truncation) lengths are {64, 96, 128}.
*/
diff --git a/core/java/android/net/IpSecManager.java b/core/java/android/net/IpSecManager.java
index d7b3256..eccd5f4 100644
--- a/core/java/android/net/IpSecManager.java
+++ b/core/java/android/net/IpSecManager.java
@@ -136,7 +136,7 @@
}
@Override
- protected void finalize() {
+ protected void finalize() throws Throwable {
if (mCloseGuard != null) {
mCloseGuard.warnIfOpen();
}
diff --git a/core/java/android/net/LocalSocketImpl.java b/core/java/android/net/LocalSocketImpl.java
index 05c8afb..6e4a231 100644
--- a/core/java/android/net/LocalSocketImpl.java
+++ b/core/java/android/net/LocalSocketImpl.java
@@ -16,18 +16,18 @@
package android.net;
-import java.io.IOException;
-import java.io.OutputStream;
-import java.io.InputStream;
-import java.io.FileDescriptor;
-import java.net.SocketOptions;
-
import android.system.ErrnoException;
+import android.system.Int32Ref;
import android.system.Os;
import android.system.OsConstants;
import android.system.StructLinger;
import android.system.StructTimeval;
-import android.util.MutableInt;
+
+import java.io.FileDescriptor;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.SocketOptions;
/**
* Socket implementation used for android.net.LocalSocket and
@@ -62,7 +62,7 @@
FileDescriptor myFd = fd;
if (myFd == null) throw new IOException("socket closed");
- MutableInt avail = new MutableInt(0);
+ Int32Ref avail = new Int32Ref(0);
try {
Os.ioctlInt(myFd, OsConstants.FIONREAD, avail);
} catch (ErrnoException e) {
@@ -167,7 +167,7 @@
if (myFd == null) throw new IOException("socket closed");
// Loop until the output buffer is empty.
- MutableInt pending = new MutableInt(0);
+ Int32Ref pending = new Int32Ref(0);
while (true) {
try {
// See linux/net/unix/af_unix.c
diff --git a/core/java/android/net/MacAddress.java b/core/java/android/net/MacAddress.java
new file mode 100644
index 0000000..f6a69ba
--- /dev/null
+++ b/core/java/android/net/MacAddress.java
@@ -0,0 +1,274 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.util.BitUtils;
+
+import java.util.Arrays;
+import java.util.Random;
+import java.util.StringJoiner;
+
+/**
+ * Represents a mac address.
+ *
+ * @hide
+ */
+public final class MacAddress implements Parcelable {
+
+ private static final int ETHER_ADDR_LEN = 6;
+ private static final byte[] ETHER_ADDR_BROADCAST = addr(0xff, 0xff, 0xff, 0xff, 0xff, 0xff);
+
+ /** The broadcast mac address. */
+ public static final MacAddress BROADCAST_ADDRESS = new MacAddress(ETHER_ADDR_BROADCAST);
+
+ /** The zero mac address. */
+ public static final MacAddress ALL_ZEROS_ADDRESS = new MacAddress(0);
+
+ /** Represents categories of mac addresses. */
+ public enum MacAddressType {
+ UNICAST,
+ MULTICAST,
+ BROADCAST;
+ }
+
+ private static final long VALID_LONG_MASK = BROADCAST_ADDRESS.mAddr;
+ private static final long LOCALLY_ASSIGNED_MASK = new MacAddress("2:0:0:0:0:0").mAddr;
+ private static final long MULTICAST_MASK = new MacAddress("1:0:0:0:0:0").mAddr;
+ private static final long OUI_MASK = new MacAddress("ff:ff:ff:0:0:0").mAddr;
+ private static final long NIC_MASK = new MacAddress("0:0:0:ff:ff:ff").mAddr;
+ private static final MacAddress BASE_ANDROID_MAC = new MacAddress("da:a1:19:0:0:0");
+
+ // Internal representation of the mac address as a single 8 byte long.
+ // The encoding scheme sets the two most significant bytes to 0. The 6 bytes of the
+ // mac address are encoded in the 6 least significant bytes of the long, where the first
+ // byte of the array is mapped to the 3rd highest logical byte of the long, the second
+ // byte of the array is mapped to the 4th highest logical byte of the long, and so on.
+ private final long mAddr;
+
+ private MacAddress(long addr) {
+ mAddr = addr;
+ }
+
+ /** Creates a MacAddress for the given byte representation. */
+ public MacAddress(byte[] addr) {
+ this(longAddrFromByteAddr(addr));
+ }
+
+ /** Creates a MacAddress for the given string representation. */
+ public MacAddress(String addr) {
+ this(longAddrFromByteAddr(byteAddrFromStringAddr(addr)));
+ }
+
+ /** Returns the MacAddressType of this MacAddress. */
+ public MacAddressType addressType() {
+ if (equals(BROADCAST_ADDRESS)) {
+ return MacAddressType.BROADCAST;
+ }
+ if (isMulticastAddress()) {
+ return MacAddressType.MULTICAST;
+ }
+ return MacAddressType.UNICAST;
+ }
+
+ /** Returns true if this MacAddress corresponds to a multicast address. */
+ public boolean isMulticastAddress() {
+ return (mAddr & MULTICAST_MASK) != 0;
+ }
+
+ /** Returns true if this MacAddress corresponds to a locally assigned address. */
+ public boolean isLocallyAssigned() {
+ return (mAddr & LOCALLY_ASSIGNED_MASK) != 0;
+ }
+
+ /** Returns a byte array representation of this MacAddress. */
+ public byte[] toByteArray() {
+ return byteAddrFromLongAddr(mAddr);
+ }
+
+ @Override
+ public String toString() {
+ return stringAddrFromByteAddr(byteAddrFromLongAddr(mAddr));
+ }
+
+ @Override
+ public int hashCode() {
+ return (int) ((mAddr >> 32) ^ mAddr);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ return (o instanceof MacAddress) && ((MacAddress) o).mAddr == mAddr;
+ }
+
+ @Override
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeLong(mAddr);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ public static final Parcelable.Creator<MacAddress> CREATOR =
+ new Parcelable.Creator<MacAddress>() {
+ public MacAddress createFromParcel(Parcel in) {
+ return new MacAddress(in.readLong());
+ }
+
+ public MacAddress[] newArray(int size) {
+ return new MacAddress[size];
+ }
+ };
+
+ /** Return true if the given byte array is not null and has the length of a mac address. */
+ public static boolean isMacAddress(byte[] addr) {
+ return addr != null && addr.length == ETHER_ADDR_LEN;
+ }
+
+ /**
+ * Return the MacAddressType of the mac address represented by the given byte array,
+ * or null if the given byte array does not represent an mac address.
+ */
+ public static MacAddressType macAddressType(byte[] addr) {
+ if (!isMacAddress(addr)) {
+ return null;
+ }
+ return new MacAddress(addr).addressType();
+ }
+
+ /** DOCME */
+ public static byte[] byteAddrFromStringAddr(String addr) {
+ if (addr == null) {
+ throw new IllegalArgumentException("cannot convert the null String");
+ }
+ String[] parts = addr.split(":");
+ if (parts.length != ETHER_ADDR_LEN) {
+ throw new IllegalArgumentException(addr + " was not a valid MAC address");
+ }
+ byte[] bytes = new byte[ETHER_ADDR_LEN];
+ for (int i = 0; i < ETHER_ADDR_LEN; i++) {
+ int x = Integer.valueOf(parts[i], 16);
+ if (x < 0 || 0xff < x) {
+ throw new IllegalArgumentException(addr + "was not a valid MAC address");
+ }
+ bytes[i] = (byte) x;
+ }
+ return bytes;
+ }
+
+ /** DOCME */
+ public static String stringAddrFromByteAddr(byte[] addr) {
+ if (!isMacAddress(addr)) {
+ return null;
+ }
+ StringJoiner j = new StringJoiner(":");
+ for (byte b : addr) {
+ j.add(Integer.toHexString(BitUtils.uint8(b)));
+ }
+ return j.toString();
+ }
+
+ /** @hide */
+ public static byte[] byteAddrFromLongAddr(long addr) {
+ byte[] bytes = new byte[ETHER_ADDR_LEN];
+ int index = ETHER_ADDR_LEN;
+ while (index-- > 0) {
+ bytes[index] = (byte) addr;
+ addr = addr >> 8;
+ }
+ return bytes;
+ }
+
+ /** @hide */
+ public static long longAddrFromByteAddr(byte[] addr) {
+ if (!isMacAddress(addr)) {
+ throw new IllegalArgumentException(
+ Arrays.toString(addr) + " was not a valid MAC address");
+ }
+ long longAddr = 0;
+ for (byte b : addr) {
+ longAddr = (longAddr << 8) + BitUtils.uint8(b);
+ }
+ return longAddr;
+ }
+
+ /** @hide */
+ public static long longAddrFromStringAddr(String addr) {
+ if (addr == null) {
+ throw new IllegalArgumentException("cannot convert the null String");
+ }
+ String[] parts = addr.split(":");
+ if (parts.length != ETHER_ADDR_LEN) {
+ throw new IllegalArgumentException(addr + " was not a valid MAC address");
+ }
+ long longAddr = 0;
+ int index = ETHER_ADDR_LEN;
+ while (index-- > 0) {
+ int x = Integer.valueOf(parts[index], 16);
+ if (x < 0 || 0xff < x) {
+ throw new IllegalArgumentException(addr + "was not a valid MAC address");
+ }
+ longAddr = x + (longAddr << 8);
+ }
+ return longAddr;
+ }
+
+ /** @hide */
+ public static String stringAddrFromLongAddr(long addr) {
+ addr = Long.reverseBytes(addr) >> 16;
+ StringJoiner j = new StringJoiner(":");
+ for (int i = 0; i < ETHER_ADDR_LEN; i++) {
+ j.add(Integer.toHexString((byte) addr));
+ addr = addr >> 8;
+ }
+ return j.toString();
+ }
+
+ /**
+ * Returns a randomely generated mac address with the Android OUI value "DA-A1-19".
+ * The locally assigned bit is always set to 1.
+ */
+ public static MacAddress getRandomAddress() {
+ return getRandomAddress(BASE_ANDROID_MAC, new Random());
+ }
+
+ /**
+ * Returns a randomely generated mac address using the given Random object and the same
+ * OUI values as the given MacAddress. The locally assigned bit is always set to 1.
+ */
+ public static MacAddress getRandomAddress(MacAddress base, Random r) {
+ long longAddr = (base.mAddr & OUI_MASK) | (NIC_MASK & r.nextLong()) | LOCALLY_ASSIGNED_MASK;
+ return new MacAddress(longAddr);
+ }
+
+ // Convenience function for working around the lack of byte literals.
+ private static byte[] addr(int... in) {
+ if (in.length != ETHER_ADDR_LEN) {
+ throw new IllegalArgumentException(Arrays.toString(in)
+ + " was not an array with length equal to " + ETHER_ADDR_LEN);
+ }
+ byte[] out = new byte[ETHER_ADDR_LEN];
+ for (int i = 0; i < ETHER_ADDR_LEN; i++) {
+ out[i] = (byte) in[i];
+ }
+ return out;
+ }
+}
diff --git a/core/java/android/net/NetworkCapabilities.java b/core/java/android/net/NetworkCapabilities.java
index db12dd9..ee75fd4 100644
--- a/core/java/android/net/NetworkCapabilities.java
+++ b/core/java/android/net/NetworkCapabilities.java
@@ -17,6 +17,7 @@
package android.net;
import android.annotation.IntDef;
+import android.net.ConnectivityManager.NetworkCallback;
import android.os.Parcel;
import android.os.Parcelable;
@@ -30,15 +31,24 @@
import java.util.StringJoiner;
/**
- * This class represents the capabilities of a network. This is used both to specify
- * needs to {@link ConnectivityManager} and when inspecting a network.
- *
- * Note that this replaces the old {@link ConnectivityManager#TYPE_MOBILE} method
- * of network selection. Rather than indicate a need for Wi-Fi because an application
- * needs high bandwidth and risk obsolescence when a new, fast network appears (like LTE),
- * the application should specify it needs high bandwidth. Similarly if an application
- * needs an unmetered network for a bulk transfer it can specify that rather than assuming
- * all cellular based connections are metered and all Wi-Fi based connections are not.
+ * Representation of the capabilities of a network. This object serves two
+ * purposes:
+ * <ul>
+ * <li>An expression of the current capabilities of an active network, typically
+ * expressed through
+ * {@link NetworkCallback#onCapabilitiesChanged(Network, NetworkCapabilities)}
+ * or {@link ConnectivityManager#getNetworkCapabilities(Network)}.
+ * <li>An expression of the future capabilities of a desired network, typically
+ * expressed through {@link NetworkRequest}.
+ * </ul>
+ * <p>
+ * This replaces the old {@link ConnectivityManager#TYPE_MOBILE} method of
+ * network selection. Rather than indicate a need for Wi-Fi because an
+ * application needs high bandwidth and risk obsolescence when a new, fast
+ * network appears (like LTE), the application should specify it needs high
+ * bandwidth. Similarly if an application needs an unmetered network for a bulk
+ * transfer it can specify that rather than assuming all cellular based
+ * connections are metered and all Wi-Fi based connections are not.
*/
public final class NetworkCapabilities implements Parcelable {
private static final String TAG = "NetworkCapabilities";
@@ -101,6 +111,7 @@
NET_CAPABILITY_NOT_VPN,
NET_CAPABILITY_VALIDATED,
NET_CAPABILITY_CAPTIVE_PORTAL,
+ NET_CAPABILITY_NOT_ROAMING,
NET_CAPABILITY_FOREGROUND,
})
public @interface NetCapability { }
@@ -218,11 +229,16 @@
public static final int NET_CAPABILITY_CAPTIVE_PORTAL = 17;
/**
+ * Indicates that this network is not roaming.
+ */
+ public static final int NET_CAPABILITY_NOT_ROAMING = 18;
+
+ /**
* Indicates that this network is available for use by apps, and not a network that is being
* kept up in the background to facilitate fast network switching.
* @hide
*/
- public static final int NET_CAPABILITY_FOREGROUND = 18;
+ public static final int NET_CAPABILITY_FOREGROUND = 19;
private static final int MIN_NET_CAPABILITY = NET_CAPABILITY_MMS;
private static final int MAX_NET_CAPABILITY = NET_CAPABILITY_FOREGROUND;
@@ -237,6 +253,7 @@
(1 << NET_CAPABILITY_TRUSTED) |
(1 << NET_CAPABILITY_VALIDATED) |
(1 << NET_CAPABILITY_CAPTIVE_PORTAL) |
+ (1 << NET_CAPABILITY_NOT_ROAMING) |
(1 << NET_CAPABILITY_FOREGROUND);
/**
@@ -316,6 +333,21 @@
}
/**
+ * Sets (or clears) the given capability on this {@link NetworkCapabilities}
+ * instance.
+ *
+ * @hide
+ */
+ public NetworkCapabilities setCapability(@NetCapability int capability, boolean value) {
+ if (value) {
+ addCapability(capability);
+ } else {
+ removeCapability(capability);
+ }
+ return this;
+ }
+
+ /**
* Gets all the capabilities set on this {@code NetworkCapability} instance.
*
* @return an array of capability values for this instance.
@@ -326,6 +358,15 @@
}
/**
+ * Sets all the capabilities set on this {@code NetworkCapability} instance.
+ *
+ * @hide
+ */
+ public void setCapabilities(@NetCapability int[] capabilities) {
+ mNetworkCapabilities = BitUtils.packBits(capabilities);
+ }
+
+ /**
* Tests for the presence of a capabilitity on this instance.
*
* @param capability the capabilities to be tested for.
@@ -515,6 +556,21 @@
}
/**
+ * Sets (or clears) the given transport on this {@link NetworkCapabilities}
+ * instance.
+ *
+ * @hide
+ */
+ public NetworkCapabilities setTransportType(@Transport int transportType, boolean value) {
+ if (value) {
+ addTransportType(transportType);
+ } else {
+ removeTransportType(transportType);
+ }
+ return this;
+ }
+
+ /**
* Gets all the transports set on this {@code NetworkCapability} instance.
*
* @return an array of transport type values for this instance.
@@ -525,6 +581,15 @@
}
/**
+ * Sets all the transports set on this {@code NetworkCapability} instance.
+ *
+ * @hide
+ */
+ public void setTransportTypes(@Transport int[] transportTypes) {
+ mTransportTypes = BitUtils.packBits(transportTypes);
+ }
+
+ /**
* Tests for the presence of a transport on this instance.
*
* @param transportType the transport type to be tested for.
@@ -549,12 +614,18 @@
}
/**
+ * Value indicating that link bandwidth is unspecified.
+ * @hide
+ */
+ public static final int LINK_BANDWIDTH_UNSPECIFIED = 0;
+
+ /**
* Passive link bandwidth. This is a rough guide of the expected peak bandwidth
* for the first hop on the given transport. It is not measured, but may take into account
* link parameters (Radio technology, allocated channels, etc).
*/
- private int mLinkUpBandwidthKbps;
- private int mLinkDownBandwidthKbps;
+ private int mLinkUpBandwidthKbps = LINK_BANDWIDTH_UNSPECIFIED;
+ private int mLinkDownBandwidthKbps = LINK_BANDWIDTH_UNSPECIFIED;
/**
* Sets the upstream bandwidth for this network in Kbps. This always only refers to
@@ -571,8 +642,9 @@
* @param upKbps the estimated first hop upstream (device to network) bandwidth.
* @hide
*/
- public void setLinkUpstreamBandwidthKbps(int upKbps) {
+ public NetworkCapabilities setLinkUpstreamBandwidthKbps(int upKbps) {
mLinkUpBandwidthKbps = upKbps;
+ return this;
}
/**
@@ -600,8 +672,9 @@
* @param downKbps the estimated first hop downstream (network to device) bandwidth.
* @hide
*/
- public void setLinkDownstreamBandwidthKbps(int downKbps) {
+ public NetworkCapabilities setLinkDownstreamBandwidthKbps(int downKbps) {
mLinkDownBandwidthKbps = downKbps;
+ return this;
}
/**
@@ -628,6 +701,20 @@
return (this.mLinkUpBandwidthKbps == nc.mLinkUpBandwidthKbps &&
this.mLinkDownBandwidthKbps == nc.mLinkDownBandwidthKbps);
}
+ /** @hide */
+ public static int minBandwidth(int a, int b) {
+ if (a == LINK_BANDWIDTH_UNSPECIFIED) {
+ return b;
+ } else if (b == LINK_BANDWIDTH_UNSPECIFIED) {
+ return a;
+ } else {
+ return Math.min(a, b);
+ }
+ }
+ /** @hide */
+ public static int maxBandwidth(int a, int b) {
+ return Math.max(a, b);
+ }
private NetworkSpecifier mNetworkSpecifier = null;
@@ -708,8 +795,9 @@
* @param signalStrength the bearer-specific signal strength.
* @hide
*/
- public void setSignalStrength(int signalStrength) {
+ public NetworkCapabilities setSignalStrength(int signalStrength) {
mSignalStrength = signalStrength;
+ return this;
}
/**
@@ -968,6 +1056,7 @@
case NET_CAPABILITY_NOT_VPN: return "NOT_VPN";
case NET_CAPABILITY_VALIDATED: return "VALIDATED";
case NET_CAPABILITY_CAPTIVE_PORTAL: return "CAPTIVE_PORTAL";
+ case NET_CAPABILITY_NOT_ROAMING: return "NOT_ROAMING";
case NET_CAPABILITY_FOREGROUND: return "FOREGROUND";
default: return Integer.toString(capability);
}
diff --git a/core/java/android/net/NetworkIdentity.java b/core/java/android/net/NetworkIdentity.java
index 0775bda..df404b7 100644
--- a/core/java/android/net/NetworkIdentity.java
+++ b/core/java/android/net/NetworkIdentity.java
@@ -189,7 +189,8 @@
String subscriberId = null;
String networkId = null;
- boolean roaming = false;
+ boolean roaming = !state.networkCapabilities.hasCapability(
+ NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING);
boolean metered = !state.networkCapabilities.hasCapability(
NetworkCapabilities.NET_CAPABILITY_NOT_METERED);
@@ -203,7 +204,6 @@
}
subscriberId = state.subscriberId;
- roaming = state.networkInfo.isRoaming();
} else if (type == TYPE_WIFI) {
if (state.networkId != null) {
diff --git a/core/java/android/net/NetworkInfo.java b/core/java/android/net/NetworkInfo.java
index 84c32be..d554938 100644
--- a/core/java/android/net/NetworkInfo.java
+++ b/core/java/android/net/NetworkInfo.java
@@ -307,11 +307,17 @@
}
/**
- * Indicates whether the device is currently roaming on this network.
- * When {@code true}, it suggests that use of data on this network
- * may incur extra costs.
+ * Indicates whether the device is currently roaming on this network. When
+ * {@code true}, it suggests that use of data on this network may incur
+ * extra costs.
+ *
* @return {@code true} if roaming is in effect, {@code false} otherwise.
+ * @deprecated Callers should switch to checking
+ * {@link NetworkCapabilities#NET_CAPABILITY_NOT_ROAMING}
+ * instead, since that handles more complex situations, such as
+ * VPNs.
*/
+ @Deprecated
public boolean isRoaming() {
synchronized (this) {
return mIsRoaming;
@@ -320,6 +326,7 @@
/** {@hide} */
@VisibleForTesting
+ @Deprecated
public void setRoaming(boolean isRoaming) {
synchronized (this) {
mIsRoaming = isRoaming;
diff --git a/core/java/android/net/OWNERS b/core/java/android/net/OWNERS
index 0f1e259..d1ce60e 100644
--- a/core/java/android/net/OWNERS
+++ b/core/java/android/net/OWNERS
@@ -1,6 +1,6 @@
ek@google.com
hugobenichi@google.com
-jsharkey@google.com
+jsharkey@android.com
lorenzo@google.com
satk@google.com
silberst@google.com
diff --git a/core/java/android/net/metrics/ConnectStats.java b/core/java/android/net/metrics/ConnectStats.java
index 2495cab..b320b75 100644
--- a/core/java/android/net/metrics/ConnectStats.java
+++ b/core/java/android/net/metrics/ConnectStats.java
@@ -119,7 +119,8 @@
@Override
public String toString() {
- StringBuilder builder = new StringBuilder("ConnectStats(").append(netId).append(", ");
+ StringBuilder builder =
+ new StringBuilder("ConnectStats(").append("netId=").append(netId).append(", ");
for (int t : BitUtils.unpackBits(transports)) {
builder.append(NetworkCapabilities.transportNameOf(t)).append(", ");
}
diff --git a/core/java/android/net/metrics/DefaultNetworkEvent.java b/core/java/android/net/metrics/DefaultNetworkEvent.java
index eb61c153..8ff8e4f 100644
--- a/core/java/android/net/metrics/DefaultNetworkEvent.java
+++ b/core/java/android/net/metrics/DefaultNetworkEvent.java
@@ -20,44 +20,72 @@
import android.net.NetworkCapabilities;
+import com.android.internal.util.BitUtils;
+
+import java.util.StringJoiner;
+
/**
* An event recorded by ConnectivityService when there is a change in the default network.
* {@hide}
*/
public class DefaultNetworkEvent {
- // The ID of the network that has become the new default or NETID_UNSET if none.
+ // The creation time in milliseconds of this DefaultNetworkEvent.
+ public final long creationTimeMs;
+ // The network ID of the network or NETID_UNSET if none.
public int netId = NETID_UNSET;
- // The list of transport types of the new default network, for example TRANSPORT_WIFI, as
- // defined in NetworkCapabilities.java.
- public int[] transportTypes = new int[0];
- // The ID of the network that was the default before or NETID_UNSET if none.
- public int prevNetId = NETID_UNSET;
- // Whether the previous network had IPv4/IPv6 connectivity.
- public boolean prevIPv4;
- public boolean prevIPv6;
+ // The list of transport types, as defined in NetworkCapabilities.java.
+ public int transports;
+ // The list of transport types of the last previous default network.
+ public int previousTransports;
+ // Whether the network has IPv4/IPv6 connectivity.
+ public boolean ipv4;
+ public boolean ipv6;
+ // The initial network score when this network became the default network.
+ public int initialScore;
+ // The initial network score when this network stopped being the default network.
+ public int finalScore;
+ // The total duration in milliseconds this network was the default network.
+ public long durationMs;
+ // The total duration in milliseconds this network was the default network and was validated.
+ public long validatedMs;
+
+ public DefaultNetworkEvent(long timeMs) {
+ creationTimeMs = timeMs;
+ }
+
+ /** Update the durationMs of this DefaultNetworkEvent for the given current time. */
+ public void updateDuration(long timeMs) {
+ durationMs = timeMs - creationTimeMs;
+ }
@Override
public String toString() {
- String prevNetwork = String.valueOf(prevNetId);
- String newNetwork = String.valueOf(netId);
- if (prevNetId != 0) {
- prevNetwork += ":" + ipSupport();
+ StringJoiner j = new StringJoiner(", ", "DefaultNetworkEvent(", ")");
+ j.add("netId=" + netId);
+ for (int t : BitUtils.unpackBits(transports)) {
+ j.add(NetworkCapabilities.transportNameOf(t));
}
- if (netId != 0) {
- newNetwork += ":" + NetworkCapabilities.transportNamesOf(transportTypes);
+ j.add("ip=" + ipSupport());
+ if (initialScore > 0) {
+ j.add("initial_score=" + initialScore);
}
- return String.format("DefaultNetworkEvent(%s -> %s)", prevNetwork, newNetwork);
+ if (finalScore > 0) {
+ j.add("final_score=" + finalScore);
+ }
+ j.add(String.format("duration=%.0fs", durationMs / 1000.0));
+ j.add(String.format("validation=%4.1f%%", (validatedMs * 100.0) / durationMs));
+ return j.toString();
}
private String ipSupport() {
- if (prevIPv4 && prevIPv6) {
+ if (ipv4 && ipv6) {
return "IPv4v6";
}
- if (prevIPv6) {
+ if (ipv6) {
return "IPv6";
}
- if (prevIPv4) {
+ if (ipv4) {
return "IPv4";
}
return "NONE";
diff --git a/core/java/android/net/metrics/DnsEvent.java b/core/java/android/net/metrics/DnsEvent.java
index 81b098b..5aa705b 100644
--- a/core/java/android/net/metrics/DnsEvent.java
+++ b/core/java/android/net/metrics/DnsEvent.java
@@ -85,7 +85,8 @@
@Override
public String toString() {
- StringBuilder builder = new StringBuilder("DnsEvent(").append(netId).append(", ");
+ StringBuilder builder =
+ new StringBuilder("DnsEvent(").append("netId=").append(netId).append(", ");
for (int t : BitUtils.unpackBits(transports)) {
builder.append(NetworkCapabilities.transportNameOf(t)).append(", ");
}
diff --git a/core/java/android/os/Debug.java b/core/java/android/os/Debug.java
index 75fea52..3286e6e 100644
--- a/core/java/android/os/Debug.java
+++ b/core/java/android/os/Debug.java
@@ -16,14 +16,16 @@
package android.os;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.app.AppGlobals;
import android.content.Context;
import android.util.Log;
import com.android.internal.util.FastPrintWriter;
+import com.android.internal.util.Preconditions;
import com.android.internal.util.TypedProperties;
-import dalvik.bytecode.OpcodeInfo;
import dalvik.system.VMDebug;
import org.apache.harmony.dalvik.ddmc.Chunk;
@@ -48,8 +50,6 @@
import java.util.Map;
-
-
/**
* Provides various debugging methods for Android applications, including
* tracing and allocation counts.
@@ -1948,13 +1948,7 @@
*/
@Deprecated
public static class InstructionCount {
- private static final int NUM_INSTR =
- OpcodeInfo.MAXIMUM_PACKED_VALUE + 1;
-
- private int[] mCounts;
-
public InstructionCount() {
- mCounts = new int[NUM_INSTR];
}
/**
@@ -1964,13 +1958,7 @@
* @return true if counting was started
*/
public boolean resetAndStart() {
- try {
- VMDebug.startInstructionCounting();
- VMDebug.resetInstructionCount();
- } catch (UnsupportedOperationException uoe) {
- return false;
- }
- return true;
+ return false;
}
/**
@@ -1978,13 +1966,7 @@
* counting process.
*/
public boolean collect() {
- try {
- VMDebug.stopInstructionCounting();
- VMDebug.getInstructionCount(mCounts);
- } catch (UnsupportedOperationException uoe) {
- return false;
- }
- return true;
+ return false;
}
/**
@@ -1992,13 +1974,7 @@
* all threads).
*/
public int globalTotal() {
- int count = 0;
-
- for (int i = 0; i < NUM_INSTR; i++) {
- count += mCounts[i];
- }
-
- return count;
+ return 0;
}
/**
@@ -2006,15 +1982,7 @@
* executed globally.
*/
public int globalMethodInvocations() {
- int count = 0;
-
- for (int i = 0; i < NUM_INSTR; i++) {
- if (OpcodeInfo.isInvoke(i)) {
- count += mCounts[i];
- }
- }
-
- return count;
+ return 0;
}
}
@@ -2371,4 +2339,24 @@
public static String getCaller() {
return getCaller(Thread.currentThread().getStackTrace(), 0);
}
+
+ /**
+ * Attach a library as a jvmti agent to the current runtime.
+ *
+ * @param library library containing the agent
+ * @param options options passed to the agent
+ *
+ * @throws IOException If the agent could not be attached
+ */
+ public static void attachJvmtiAgent(@NonNull String library, @Nullable String options)
+ throws IOException {
+ Preconditions.checkNotNull(library);
+ Preconditions.checkArgument(!library.contains("="));
+
+ if (options == null) {
+ VMDebug.attachAgent(library);
+ } else {
+ VMDebug.attachAgent(library + "=" + options);
+ }
+ }
}
diff --git a/core/java/android/os/HidlSupport.java b/core/java/android/os/HidlSupport.java
index 7dec4d7..3544ea1 100644
--- a/core/java/android/os/HidlSupport.java
+++ b/core/java/android/os/HidlSupport.java
@@ -156,4 +156,27 @@
// Should not reach here.
throw new UnsupportedOperationException();
}
+
+ /**
+ * Test that two interfaces are equal. This is the Java equivalent to C++
+ * interfacesEqual function.
+ * This essentially calls .equals on the internal binder objects (via Binder()).
+ * - If both interfaces are proxies, asBinder() returns a {@link HwRemoteBinder}
+ * object, and they are compared in {@link HwRemoteBinder#equals}.
+ * - If both interfaces are stubs, asBinder() returns the object itself. By default,
+ * auto-generated IFoo.Stub does not override equals(), but an implementation can
+ * optionally override it, and {@code interfacesEqual} will use it here.
+ */
+ public static boolean interfacesEqual(IHwInterface lft, Object rgt) {
+ if (lft == rgt) {
+ return true;
+ }
+ if (lft == null || rgt == null) {
+ return false;
+ }
+ if (!(rgt instanceof IHwInterface)) {
+ return false;
+ }
+ return Objects.equals(lft.asBinder(), ((IHwInterface) rgt).asBinder());
+ }
}
diff --git a/core/java/android/os/HwBinder.java b/core/java/android/os/HwBinder.java
index 270e63f..5e2a081 100644
--- a/core/java/android/os/HwBinder.java
+++ b/core/java/android/os/HwBinder.java
@@ -16,10 +16,10 @@
package android.os;
-import java.util.ArrayList;
-import java.util.NoSuchElementException;
import libcore.util.NativeAllocationRegistry;
+import java.util.NoSuchElementException;
+
/** @hide */
public abstract class HwBinder implements IHwBinder {
private static final String TAG = "HwBinder";
@@ -46,9 +46,16 @@
public native final void registerService(String serviceName)
throws RemoteException;
- public static native final IHwBinder getService(
+ public static final IHwBinder getService(
String iface,
String serviceName)
+ throws RemoteException, NoSuchElementException {
+ return getService(iface, serviceName, false /* retry */);
+ }
+ public static native final IHwBinder getService(
+ String iface,
+ String serviceName,
+ boolean retry)
throws RemoteException, NoSuchElementException;
public static native final void configureRpcThreadpool(
diff --git a/core/java/android/os/HwBlob.java b/core/java/android/os/HwBlob.java
index 88226f0..5e9b9ae3 100644
--- a/core/java/android/os/HwBlob.java
+++ b/core/java/android/os/HwBlob.java
@@ -43,6 +43,18 @@
public native final double getDouble(long offset);
public native final String getString(long offset);
+ /**
+ The copyTo... methods copy the blob's data, starting from the given
+ byte offset, into the array. A total of "size" _elements_ are copied.
+ */
+ public native final void copyToBoolArray(long offset, boolean[] array, int size);
+ public native final void copyToInt8Array(long offset, byte[] array, int size);
+ public native final void copyToInt16Array(long offset, short[] array, int size);
+ public native final void copyToInt32Array(long offset, int[] array, int size);
+ public native final void copyToInt64Array(long offset, long[] array, int size);
+ public native final void copyToFloatArray(long offset, float[] array, int size);
+ public native final void copyToDoubleArray(long offset, double[] array, int size);
+
public native final void putBool(long offset, boolean x);
public native final void putInt8(long offset, byte x);
public native final void putInt16(long offset, short x);
@@ -52,6 +64,14 @@
public native final void putDouble(long offset, double x);
public native final void putString(long offset, String x);
+ public native final void putBoolArray(long offset, boolean[] x);
+ public native final void putInt8Array(long offset, byte[] x);
+ public native final void putInt16Array(long offset, short[] x);
+ public native final void putInt32Array(long offset, int[] x);
+ public native final void putInt64Array(long offset, long[] x);
+ public native final void putFloatArray(long offset, float[] x);
+ public native final void putDoubleArray(long offset, double[] x);
+
public native final void putBlob(long offset, HwBlob blob);
public native final long handle();
diff --git a/core/java/android/os/HwRemoteBinder.java b/core/java/android/os/HwRemoteBinder.java
index 2f89ce6..a07e42c 100644
--- a/core/java/android/os/HwRemoteBinder.java
+++ b/core/java/android/os/HwRemoteBinder.java
@@ -63,4 +63,9 @@
}
private long mNativeContext;
+
+ @Override
+ public final native boolean equals(Object other);
+ @Override
+ public final native int hashCode();
}
diff --git a/core/java/android/os/ParcelFileDescriptor.aidl b/core/java/android/os/ParcelFileDescriptor.aidl
index 5857aae..6bbd99e 100644
--- a/core/java/android/os/ParcelFileDescriptor.aidl
+++ b/core/java/android/os/ParcelFileDescriptor.aidl
@@ -17,4 +17,4 @@
package android.os;
-parcelable ParcelFileDescriptor;
+parcelable ParcelFileDescriptor cpp_header "android/os/parcel_file_descriptor.h";
diff --git a/core/java/android/os/ParcelUuid.aidl b/core/java/android/os/ParcelUuid.aidl
index f7e080a..6f36297 100644
--- a/core/java/android/os/ParcelUuid.aidl
+++ b/core/java/android/os/ParcelUuid.aidl
@@ -16,4 +16,4 @@
package android.os;
-parcelable ParcelUuid;
+parcelable ParcelUuid cpp_header "android/os/parcel_uuid.h";
diff --git a/core/java/android/os/TokenWatcher.java b/core/java/android/os/TokenWatcher.java
index 9b3a2d6..00333dad 100644
--- a/core/java/android/os/TokenWatcher.java
+++ b/core/java/android/os/TokenWatcher.java
@@ -16,17 +16,23 @@
package android.os;
-import java.io.PrintWriter;
-import java.util.ArrayList;
-import java.util.WeakHashMap;
-import java.util.Set;
import android.util.Log;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Set;
+import java.util.WeakHashMap;
+
/**
- * Helper class that helps you use IBinder objects as reference counted
- * tokens. IBinders make good tokens because we find out when they are
- * removed
+ * A TokenWatcher watches a collection of {@link IBinder}s. IBinders are added
+ * to the collection by calling {@link #acquire}, and removed by calling {@link
+ * #release}. IBinders are also implicitly removed when they become weakly
+ * reachable. Each IBinder may be added at most once.
*
+ * The {@link #acquired} method is invoked by posting to the specified handler
+ * whenever the size of the watched collection becomes nonzero. The {@link
+ * #released} method is invoked on the specified handler whenever the size of
+ * the watched collection becomes zero.
*/
public abstract class TokenWatcher
{
@@ -59,15 +65,23 @@
* Record that this token has been acquired. When acquire is called, and
* the current count is 0, the acquired method is called on the given
* handler.
- *
- * @param token An IBinder object. If this token has already been acquired,
- * no action is taken.
+ *
+ * Note that the same {@code token} can only be acquired once. If this
+ * {@code token} has already been acquired, no action is taken. The first
+ * subsequent call to {@link #release} will release this {@code token}
+ * immediately.
+ *
+ * @param token An IBinder object.
* @param tag A string used by the {@link #dump} method for debugging,
* to see who has references.
*/
public void acquire(IBinder token, String tag)
{
synchronized (mTokens) {
+ if (mTokens.containsKey(token)) {
+ return;
+ }
+
// explicitly checked to avoid bogus sendNotification calls because
// of the WeakHashMap and the GC
int oldSize = mTokens.size();
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index cbeeabe..6981ffc 100755
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -9158,11 +9158,20 @@
public static final String DEFAULT_DNS_SERVER = "default_dns_server";
/**
- * Whether to disable DNS over TLS (boolean)
+ * The requested Private DNS mode (string), and an accompanying specifier (string).
+ *
+ * Currently, the specifier holds the chosen provider name when the mode requests
+ * a specific provider. It may be used to store the provider name even when the
+ * mode changes so that temporarily disabling and re-enabling the specific
+ * provider mode does not necessitate retyping the provider hostname.
*
* @hide
*/
- public static final String DNS_TLS_DISABLED = "dns_tls_disabled";
+ public static final String PRIVATE_DNS_MODE = "private_dns_mode";
+ /**
+ * @hide
+ */
+ public static final String PRIVATE_DNS_SPECIFIER = "private_dns_specifier";
/** {@hide} */
public static final String
@@ -10204,7 +10213,9 @@
DOCK_AUDIO_MEDIA_ENABLED,
ENCODED_SURROUND_OUTPUT,
LOW_POWER_MODE_TRIGGER_LEVEL,
- BLUETOOTH_ON
+ BLUETOOTH_ON,
+ PRIVATE_DNS_MODE,
+ PRIVATE_DNS_SPECIFIER
};
private static final ContentProviderHolder sProviderHolder =
diff --git a/core/java/android/view/WindowManagerGlobal.java b/core/java/android/view/WindowManagerGlobal.java
index c7e8dee..cca66d6 100644
--- a/core/java/android/view/WindowManagerGlobal.java
+++ b/core/java/android/view/WindowManagerGlobal.java
@@ -605,9 +605,10 @@
public void setStoppedState(IBinder token, boolean stopped) {
synchronized (mLock) {
int count = mViews.size();
- for (int i = 0; i < count; i++) {
+ for (int i = count - 1; i >= 0; i--) {
if (token == null || mParams.get(i).token == token) {
ViewRootImpl root = mRoots.get(i);
+ // Client might remove the view by "stopped" event.
root.setWindowStopped(stopped);
}
}
diff --git a/core/java/com/android/internal/app/IMediaContainerService.aidl b/core/java/com/android/internal/app/IMediaContainerService.aidl
index 81ea191..36e4c1c6 100644
--- a/core/java/com/android/internal/app/IMediaContainerService.aidl
+++ b/core/java/com/android/internal/app/IMediaContainerService.aidl
@@ -27,9 +27,6 @@
PackageInfoLite getMinimalPackageInfo(String packagePath, int flags, String abiOverride);
ObbInfo getObbInfo(String filename);
- long calculateDirectorySize(String directory);
- /** Return file system stats: [0] is total bytes, [1] is available bytes */
- long[] getFileSystemStats(String path);
void clearDirectory(String directory);
long calculateInstalledSize(String packagePath, boolean isForwardLocked, String abiOverride);
}
diff --git a/core/java/com/android/internal/net/OWNERS b/core/java/com/android/internal/net/OWNERS
index 7cb32ff..e2064a8 100644
--- a/core/java/com/android/internal/net/OWNERS
+++ b/core/java/com/android/internal/net/OWNERS
@@ -2,5 +2,5 @@
ek@google.com
hugobenichi@google.com
-jsharkey@google.com
+jsharkey@android.com
lorenzo@google.com
diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java
index 6d8b811..3cda820 100644
--- a/core/java/com/android/internal/os/ZygoteInit.java
+++ b/core/java/com/android/internal/os/ZygoteInit.java
@@ -546,13 +546,14 @@
for (String classPathElement : classPathElements) {
// System server is fully AOTed and never profiled
// for profile guided compilation.
- // TODO: Make this configurable between INTERPRET_ONLY, SPEED, SPACE and EVERYTHING?
+ String systemServerFilter = SystemProperties.get(
+ "dalvik.vm.systemservercompilerfilter", "speed");
int dexoptNeeded;
try {
dexoptNeeded = DexFile.getDexOptNeeded(
- classPathElement, instructionSet, "speed",
- false /* newProfile */, false /* downgrade */);
+ classPathElement, instructionSet, systemServerFilter,
+ null /* classLoaderContext */, false /* newProfile */, false /* downgrade */);
} catch (FileNotFoundException ignored) {
// Do not add to the classpath.
Log.w(TAG, "Missing classpath element for system server: " + classPathElement);
@@ -570,13 +571,13 @@
final String packageName = "*";
final String outputPath = null;
final int dexFlags = 0;
- final String compilerFilter = "speed";
+ final String compilerFilter = systemServerFilter;
final String uuid = StorageManager.UUID_PRIVATE_INTERNAL;
final String seInfo = null;
try {
installd.dexopt(classPathElement, Process.SYSTEM_UID, packageName,
instructionSet, dexoptNeeded, outputPath, dexFlags, compilerFilter,
- uuid, sharedLibraries, seInfo);
+ uuid, sharedLibraries, seInfo, false /* downgrade */);
} catch (RemoteException | ServiceSpecificException e) {
// Ignore (but log), we need this on the classpath for fallback mode.
Log.w(TAG, "Failed compiling classpath element for system server: "
diff --git a/core/java/com/android/internal/util/StateMachine.java b/core/java/com/android/internal/util/StateMachine.java
index 8d9630f..e5ad1f4 100644
--- a/core/java/com/android/internal/util/StateMachine.java
+++ b/core/java/com/android/internal/util/StateMachine.java
@@ -804,7 +804,7 @@
/** State that processed the message */
State msgProcessedState = null;
- if (mIsConstructionCompleted) {
+ if (mIsConstructionCompleted || (mMsg.what == SM_QUIT_CMD)) {
/** Normal path */
msgProcessedState = processMsg(msg);
} else if (!mIsConstructionCompleted && (mMsg.what == SM_INIT_CMD)
diff --git a/core/jni/android_os_HwBinder.cpp b/core/jni/android_os_HwBinder.cpp
index c4f22ee..08d9527 100644
--- a/core/jni/android_os_HwBinder.cpp
+++ b/core/jni/android_os_HwBinder.cpp
@@ -296,7 +296,8 @@
JNIEnv *env,
jclass /* clazzObj */,
jstring ifaceNameObj,
- jstring serviceNameObj) {
+ jstring serviceNameObj,
+ jboolean retry) {
using ::android::hidl::base::V1_0::IBase;
using ::android::hardware::details::getRawServiceInternal;
@@ -319,8 +320,7 @@
serviceName = str.c_str();
}
- // TODO(b/67981006): true /* retry */
- sp<IBase> ret = getRawServiceInternal(ifaceName, serviceName, false /* retry */, false /* getStub */);
+ sp<IBase> ret = getRawServiceInternal(ifaceName, serviceName, retry /* retry */, false /* getStub */);
sp<hardware::IBinder> service = hardware::toBinder<hidl::base::V1_0::IBase>(ret);
if (service == NULL) {
@@ -360,7 +360,7 @@
{ "registerService", "(Ljava/lang/String;)V",
(void *)JHwBinder_native_registerService },
- { "getService", "(Ljava/lang/String;Ljava/lang/String;)L" PACKAGE_PATH "/IHwBinder;",
+ { "getService", "(Ljava/lang/String;Ljava/lang/String;Z)L" PACKAGE_PATH "/IHwBinder;",
(void *)JHwBinder_native_getService },
{ "configureRpcThreadpool", "(JZ)V",
diff --git a/core/jni/android_os_HwBlob.cpp b/core/jni/android_os_HwBlob.cpp
index 40d49b7..bb916d2 100644
--- a/core/jni/android_os_HwBlob.cpp
+++ b/core/jni/android_os_HwBlob.cpp
@@ -26,6 +26,7 @@
#include <android_runtime/AndroidRuntime.h>
#include <hidl/Status.h>
#include <nativehelper/ScopedLocalRef.h>
+#include <nativehelper/ScopedPrimitiveArray.h>
#include "core_jni_helpers.h"
@@ -60,12 +61,12 @@
JNIEnv *env, jobject thiz, const sp<JHwBlob> &context) {
sp<JHwBlob> old = (JHwBlob *)env->GetLongField(thiz, gFields.contextID);
- if (context != NULL) {
- context->incStrong(NULL /* id */);
+ if (context != nullptr) {
+ context->incStrong(nullptr /* id */);
}
- if (old != NULL) {
- old->decStrong(NULL /* id */);
+ if (old != nullptr) {
+ old->decStrong(nullptr /* id */);
}
env->SetLongField(thiz, gFields.contextID, (long)context.get());
@@ -150,6 +151,10 @@
return mBuffer;
}
+void *JHwBlob::data() {
+ return mBuffer;
+}
+
size_t JHwBlob::size() const {
return mSize;
}
@@ -242,8 +247,8 @@
static void releaseNativeContext(void *nativeContext) {
sp<JHwBlob> parcel = (JHwBlob *)nativeContext;
- if (parcel != NULL) {
- parcel->decStrong(NULL /* id */);
+ if (parcel != nullptr) {
+ parcel->decStrong(nullptr /* id */);
}
}
@@ -313,6 +318,82 @@
return env->NewStringUTF(s->c_str());
}
+#define DEFINE_BLOB_ARRAY_COPIER(Suffix,Type,NewType) \
+static void JHwBlob_native_copyTo ## Suffix ## Array( \
+ JNIEnv *env, \
+ jobject thiz, \
+ jlong offset, \
+ Type ## Array array, \
+ jint size) { \
+ if (array == nullptr) { \
+ jniThrowException(env, "java/lang/NullPointerException", nullptr); \
+ return; \
+ } \
+ \
+ if (env->GetArrayLength(array) < size) { \
+ signalExceptionForError(env, BAD_VALUE); \
+ return; \
+ } \
+ \
+ sp<JHwBlob> blob = JHwBlob::GetNativeContext(env, thiz); \
+ \
+ if ((offset + size * sizeof(Type)) > blob->size()) { \
+ signalExceptionForError(env, -ERANGE); \
+ return; \
+ } \
+ \
+ env->Set ## NewType ## ArrayRegion( \
+ array, \
+ 0 /* start */, \
+ size, \
+ reinterpret_cast<const Type *>( \
+ static_cast<const uint8_t *>(blob->data()) + offset)); \
+}
+
+DEFINE_BLOB_ARRAY_COPIER(Int8,jbyte,Byte)
+DEFINE_BLOB_ARRAY_COPIER(Int16,jshort,Short)
+DEFINE_BLOB_ARRAY_COPIER(Int32,jint,Int)
+DEFINE_BLOB_ARRAY_COPIER(Int64,jlong,Long)
+DEFINE_BLOB_ARRAY_COPIER(Float,jfloat,Float)
+DEFINE_BLOB_ARRAY_COPIER(Double,jdouble,Double)
+
+static void JHwBlob_native_copyToBoolArray(
+ JNIEnv *env,
+ jobject thiz,
+ jlong offset,
+ jbooleanArray array,
+ jint size) {
+ if (array == nullptr) {
+ jniThrowException(env, "java/lang/NullPointerException", nullptr);
+ return;
+ }
+
+ if (env->GetArrayLength(array) < size) {
+ signalExceptionForError(env, BAD_VALUE);
+ return;
+ }
+
+ sp<JHwBlob> blob = JHwBlob::GetNativeContext(env, thiz);
+
+ if ((offset + size * sizeof(bool)) > blob->size()) {
+ signalExceptionForError(env, -ERANGE);
+ return;
+ }
+
+ const bool *src =
+ reinterpret_cast<const bool *>(
+ static_cast<const uint8_t *>(blob->data()) + offset);
+
+ jboolean *dst = env->GetBooleanArrayElements(array, nullptr /* isCopy */);
+
+ for (jint i = 0; i < size; ++i) {
+ dst[i] = src[i];
+ }
+
+ env->ReleaseBooleanArrayElements(array, dst, 0 /* mode */);
+ dst = nullptr;
+}
+
#define DEFINE_BLOB_PUTTER(Suffix,Type) \
static void JHwBlob_native_put ## Suffix( \
JNIEnv *env, jobject thiz, jlong offset, Type x) { \
@@ -375,6 +456,59 @@
blob->putBlob(offset + hidl_string::kOffsetOfBuffer, subBlob);
}
+#define DEFINE_BLOB_ARRAY_PUTTER(Suffix,Type,NewType) \
+static void JHwBlob_native_put ## Suffix ## Array( \
+ JNIEnv *env, jobject thiz, jlong offset, Type ## Array array) { \
+ Scoped ## NewType ## ArrayRO autoArray(env, array); \
+ \
+ if (array == nullptr) { \
+ /* NullpointerException already pending */ \
+ return; \
+ } \
+ \
+ sp<JHwBlob> blob = JHwBlob::GetNativeContext(env, thiz); \
+ \
+ status_t err = blob->write( \
+ offset, autoArray.get(), autoArray.size() * sizeof(Type)); \
+ \
+ if (err != OK) { \
+ signalExceptionForError(env, err); \
+ } \
+}
+
+DEFINE_BLOB_ARRAY_PUTTER(Int8,jbyte,Byte)
+DEFINE_BLOB_ARRAY_PUTTER(Int16,jshort,Short)
+DEFINE_BLOB_ARRAY_PUTTER(Int32,jint,Int)
+DEFINE_BLOB_ARRAY_PUTTER(Int64,jlong,Long)
+DEFINE_BLOB_ARRAY_PUTTER(Float,jfloat,Float)
+DEFINE_BLOB_ARRAY_PUTTER(Double,jdouble,Double)
+
+static void JHwBlob_native_putBoolArray(
+ JNIEnv *env, jobject thiz, jlong offset, jbooleanArray array) {
+ ScopedBooleanArrayRO autoArray(env, array);
+
+ if (array == nullptr) {
+ /* NullpointerException already pending */
+ return;
+ }
+
+ sp<JHwBlob> blob = JHwBlob::GetNativeContext(env, thiz);
+
+ if ((offset + autoArray.size() * sizeof(bool)) > blob->size()) {
+ signalExceptionForError(env, -ERANGE);
+ return;
+ }
+
+ const jboolean *src = autoArray.get();
+
+ bool *dst = reinterpret_cast<bool *>(
+ static_cast<uint8_t *>(blob->data()) + offset);
+
+ for (size_t i = 0; i < autoArray.size(); ++i) {
+ dst[i] = src[i];
+ }
+}
+
static void JHwBlob_native_putBlob(
JNIEnv *env, jobject thiz, jlong offset, jobject blobObj) {
if (blobObj == nullptr) {
@@ -413,6 +547,14 @@
{ "getDouble", "(J)D", (void *)JHwBlob_native_getDouble },
{ "getString", "(J)Ljava/lang/String;", (void *)JHwBlob_native_getString },
+ { "copyToBoolArray", "(J[ZI)V", (void *)JHwBlob_native_copyToBoolArray },
+ { "copyToInt8Array", "(J[BI)V", (void *)JHwBlob_native_copyToInt8Array },
+ { "copyToInt16Array", "(J[SI)V", (void *)JHwBlob_native_copyToInt16Array },
+ { "copyToInt32Array", "(J[II)V", (void *)JHwBlob_native_copyToInt32Array },
+ { "copyToInt64Array", "(J[JI)V", (void *)JHwBlob_native_copyToInt64Array },
+ { "copyToFloatArray", "(J[FI)V", (void *)JHwBlob_native_copyToFloatArray },
+ { "copyToDoubleArray", "(J[DI)V", (void *)JHwBlob_native_copyToDoubleArray },
+
{ "putBool", "(JZ)V", (void *)JHwBlob_native_putBool },
{ "putInt8", "(JB)V", (void *)JHwBlob_native_putInt8 },
{ "putInt16", "(JS)V", (void *)JHwBlob_native_putInt16 },
@@ -422,6 +564,14 @@
{ "putDouble", "(JD)V", (void *)JHwBlob_native_putDouble },
{ "putString", "(JLjava/lang/String;)V", (void *)JHwBlob_native_putString },
+ { "putBoolArray", "(J[Z)V", (void *)JHwBlob_native_putBoolArray },
+ { "putInt8Array", "(J[B)V", (void *)JHwBlob_native_putInt8Array },
+ { "putInt16Array", "(J[S)V", (void *)JHwBlob_native_putInt16Array },
+ { "putInt32Array", "(J[I)V", (void *)JHwBlob_native_putInt32Array },
+ { "putInt64Array", "(J[J)V", (void *)JHwBlob_native_putInt64Array },
+ { "putFloatArray", "(J[F)V", (void *)JHwBlob_native_putFloatArray },
+ { "putDoubleArray", "(J[D)V", (void *)JHwBlob_native_putDoubleArray },
+
{ "putBlob", "(JL" PACKAGE_PATH "/HwBlob;)V",
(void *)JHwBlob_native_putBlob },
diff --git a/core/jni/android_os_HwBlob.h b/core/jni/android_os_HwBlob.h
index 39393cb..6b1db63 100644
--- a/core/jni/android_os_HwBlob.h
+++ b/core/jni/android_os_HwBlob.h
@@ -50,6 +50,8 @@
size_t offset, const android::hardware::hidl_string **s) const;
const void *data() const;
+ void *data();
+
size_t size() const;
status_t putBlob(size_t offset, const sp<JHwBlob> &blob);
diff --git a/core/jni/android_os_HwRemoteBinder.cpp b/core/jni/android_os_HwRemoteBinder.cpp
index cf59a56a..ca5e1e4 100644
--- a/core/jni/android_os_HwRemoteBinder.cpp
+++ b/core/jni/android_os_HwRemoteBinder.cpp
@@ -22,9 +22,13 @@
#include "android_os_HwParcel.h"
-#include <nativehelper/JNIHelp.h>
+#include <android/hidl/base/1.0/IBase.h>
+#include <android/hidl/base/1.0/BpHwBase.h>
+#include <android/hidl/base/1.0/BnHwBase.h>
#include <android_runtime/AndroidRuntime.h>
#include <hidl/Status.h>
+#include <hidl/HidlTransportSupport.h>
+#include <nativehelper/JNIHelp.h>
#include <nativehelper/ScopedUtfChars.h>
#include <nativehelper/ScopedLocalRef.h>
@@ -413,6 +417,44 @@
return res;
}
+static sp<hidl::base::V1_0::IBase> toIBase(JNIEnv* env, jclass hwRemoteBinderClazz, jobject jbinder)
+{
+ if (jbinder == nullptr) {
+ return nullptr;
+ }
+ if (!env->IsInstanceOf(jbinder, hwRemoteBinderClazz)) {
+ return nullptr;
+ }
+ sp<JHwRemoteBinder> context = JHwRemoteBinder::GetNativeContext(env, jbinder);
+ sp<hardware::IBinder> cbinder = context->getBinder();
+ return hardware::fromBinder<hidl::base::V1_0::IBase, hidl::base::V1_0::BpHwBase,
+ hidl::base::V1_0::BnHwBase>(cbinder);
+}
+
+// equals iff other is also a non-null android.os.HwRemoteBinder object
+// and getBinder() returns the same object.
+// In particular, if other is an android.os.HwBinder object (for stubs) then
+// it returns false.
+static jboolean JHwRemoteBinder_equals(JNIEnv* env, jobject thiz, jobject other)
+{
+ if (env->IsSameObject(thiz, other)) {
+ return true;
+ }
+ if (other == NULL) {
+ return false;
+ }
+
+ ScopedLocalRef<jclass> clazz(env, FindClassOrDie(env, CLASS_PATH));
+
+ return hardware::interfacesEqual(toIBase(env, clazz.get(), thiz), toIBase(env, clazz.get(), other));
+}
+
+static jint JHwRemoteBinder_hashCode(JNIEnv* env, jobject thiz) {
+ jlong longHash = reinterpret_cast<jlong>(
+ JHwRemoteBinder::GetNativeContext(env, thiz)->getBinder().get());
+ return static_cast<jint>(longHash ^ (longHash >> 32)); // See Long.hashCode()
+}
+
static JNINativeMethod gMethods[] = {
{ "native_init", "()J", (void *)JHwRemoteBinder_native_init },
@@ -430,6 +472,11 @@
{"unlinkToDeath",
"(Landroid/os/IHwBinder$DeathRecipient;)Z",
(void*)JHwRemoteBinder_unlinkToDeath},
+
+ {"equals", "(Ljava/lang/Object;)Z",
+ (void*)JHwRemoteBinder_equals},
+
+ {"hashCode", "()I", (void*)JHwRemoteBinder_hashCode},
};
namespace android {
diff --git a/core/jni/android_util_Binder.cpp b/core/jni/android_util_Binder.cpp
index 9f8d288..560c384 100644
--- a/core/jni/android_util_Binder.cpp
+++ b/core/jni/android_util_Binder.cpp
@@ -162,6 +162,7 @@
// Garbage collect if we've allocated at least GC_INTERVAL refs since the last time.
// TODO: Consider removing this completely. We should no longer be generating GlobalRefs
// that are reclaimed as a result of GC action.
+__attribute__((no_sanitize("unsigned-integer-overflow")))
static void gcIfManyNewRefs(JNIEnv* env)
{
uint32_t totalRefs = gNumLocalRefsCreated.load(std::memory_order_relaxed)
diff --git a/core/jni/hwbinder/EphemeralStorage.cpp b/core/jni/hwbinder/EphemeralStorage.cpp
index 4996bc8..3b18f2b 100644
--- a/core/jni/hwbinder/EphemeralStorage.cpp
+++ b/core/jni/hwbinder/EphemeralStorage.cpp
@@ -111,6 +111,7 @@
break; \
}
+__attribute__((no_sanitize("unsigned-integer-overflow")))
void EphemeralStorage::release(JNIEnv *env) {
for (size_t i = mItems.size(); i--;) {
const Item &item = mItems[i];
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 40fb6e8..e291ea6 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -2402,7 +2402,13 @@
<bool name="config_networkSamplingWakesDevice">true</bool>
- <string-array translatable="false" name="config_cdma_home_system" />
+ <!-- Home (non-roaming) values for CDMA roaming indicator.
+ Carriers can override this table by resource overlay. If not,
+ the default values come from 3GPP2 C.R1001 table
+ 8.1-1. Enhanced Roaming Indicator Number Assignments -->
+ <string-array translatable="false" name="config_cdma_home_system">
+ <item>1</item>
+ </string-array>
<!--From SmsMessage-->
<!--Support decoding the user data payload as pack GSM 8-bit (a GSM alphabet
diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
index 88f2b4b..a979ac8 100644
--- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java
+++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
@@ -177,7 +177,6 @@
Settings.Global.DNS_RESOLVER_MIN_SAMPLES,
Settings.Global.DNS_RESOLVER_SAMPLE_VALIDITY_SECONDS,
Settings.Global.DNS_RESOLVER_SUCCESS_THRESHOLD_PERCENT,
- Settings.Global.DNS_TLS_DISABLED,
Settings.Global.DOCK_SOUNDS_ENABLED_WHEN_ACCESSIBILITY,
Settings.Global.DOWNLOAD_MAX_BYTES_OVER_MOBILE,
Settings.Global.DOWNLOAD_RECOMMENDED_MAX_BYTES_OVER_MOBILE,
diff --git a/core/tests/utiltests/src/com/android/internal/util/StateMachineTest.java b/core/tests/utiltests/src/com/android/internal/util/StateMachineTest.java
index eb2a516..76aa93f 100644
--- a/core/tests/utiltests/src/com/android/internal/util/StateMachineTest.java
+++ b/core/tests/utiltests/src/com/android/internal/util/StateMachineTest.java
@@ -24,6 +24,7 @@
import android.os.Looper;
import android.os.Message;
import android.os.SystemClock;
+import android.os.test.TestLooper;
import android.test.suitebuilder.annotation.Suppress;
import com.android.internal.util.State;
@@ -343,6 +344,100 @@
}
/**
+ * Tests {@link StateMachine#quitNow()} immediately after {@link StateMachine#start()}.
+ */
+ class StateMachineQuitNowAfterStartTest extends StateMachine {
+ Collection<LogRec> mLogRecs;
+
+ StateMachineQuitNowAfterStartTest(String name, Looper looper) {
+ super(name, looper);
+ mThisSm = this;
+ setDbg(DBG);
+
+ // Setup state machine with 1 state
+ addState(mS1);
+
+ // Set the initial state
+ setInitialState(mS1);
+ }
+
+ @Override
+ public void onQuitting() {
+ tlog("onQuitting");
+ addLogRec(ON_QUITTING);
+ mLogRecs = mThisSm.copyLogRecs();
+ synchronized (mThisSm) {
+ mThisSm.notifyAll();
+ }
+ }
+
+ class S1 extends State {
+ @Override
+ public void enter() {
+ tlog("S1.enter");
+ addLogRec(ENTER);
+ }
+ @Override
+ public void exit() {
+ tlog("S1.exit");
+ addLogRec(EXIT);
+ }
+ @Override
+ public boolean processMessage(Message message) {
+ switch(message.what) {
+ // Sleep and assume the other messages will be queued up.
+ case TEST_CMD_1: {
+ tlog("TEST_CMD_1");
+ sleep(500);
+ break;
+ }
+ default: {
+ tlog("default what=" + message.what);
+ break;
+ }
+ }
+ return HANDLED;
+ }
+ }
+
+ private StateMachineQuitNowAfterStartTest mThisSm;
+ private S1 mS1 = new S1();
+ }
+
+ /**
+ * When quitNow() is called immediately after start(), the QUIT_CMD gets processed
+ * before the INIT_CMD. This test ensures that the StateMachine can gracefully handle
+ * this sequencing of messages (QUIT before INIT).
+ */
+ @SmallTest
+ public void testStateMachineQuitNowAfterStart() throws Exception {
+ if (WAIT_FOR_DEBUGGER) Debug.waitForDebugger();
+
+ TestLooper testLooper = new TestLooper();
+ StateMachineQuitNowAfterStartTest smQuitNowAfterStartTest =
+ new StateMachineQuitNowAfterStartTest(
+ "smQuitNowAfterStartTest", testLooper.getLooper());
+ smQuitNowAfterStartTest.start();
+ smQuitNowAfterStartTest.quitNow();
+ if (smQuitNowAfterStartTest.isDbg()) tlog("testStateMachineQuitNowAfterStart E");
+
+ testLooper.dispatchAll();
+ dumpLogRecs(smQuitNowAfterStartTest.mLogRecs);
+ assertEquals(2, smQuitNowAfterStartTest.mLogRecs.size());
+
+ LogRec lr;
+ Iterator<LogRec> itr = smQuitNowAfterStartTest.mLogRecs.iterator();
+ lr = itr.next();
+ assertEquals(EXIT, lr.getInfo());
+ assertEquals(smQuitNowAfterStartTest.mS1, lr.getState());
+
+ lr = itr.next();
+ assertEquals(ON_QUITTING, lr.getInfo());
+
+ if (smQuitNowAfterStartTest.isDbg()) tlog("testStateMachineQuitNowAfterStart X");
+ }
+
+ /**
* Test enter/exit can use transitionTo
*/
class StateMachineEnterExitTransitionToTest extends StateMachine {
diff --git a/libs/androidfw/ZipUtils.cpp b/libs/androidfw/ZipUtils.cpp
index 5abfc8e..5d243da 100644
--- a/libs/androidfw/ZipUtils.cpp
+++ b/libs/androidfw/ZipUtils.cpp
@@ -20,10 +20,11 @@
#define LOG_TAG "ziputil"
+#include "android-base/file.h"
#include <androidfw/ZipUtils.h>
-#include <androidfw/ZipFileRO.h>
#include <utils/Log.h>
#include <utils/Compat.h>
+#include <ziparchive/zip_archive.h>
#include <stdlib.h>
#include <string.h>
@@ -33,211 +34,121 @@
using namespace android;
-static inline unsigned long get4LE(const unsigned char* buf) {
- return buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24);
-}
-
-
-static const unsigned long kReadBufSize = 32768;
-
-/*
- * Utility function that expands zip/gzip "deflate" compressed data
- * into a buffer.
- *
- * (This is a clone of the previous function, but it takes a FILE* instead
- * of an fd. We could pass fileno(fd) to the above, but we can run into
- * trouble when "fp" has a different notion of what fd's file position is.)
- *
- * "fp" is an open file positioned at the start of the "deflate" data
- * "buf" must hold at least "uncompressedLen" bytes.
- */
-/*static*/ template<typename T> bool inflateToBuffer(T& reader, void* buf,
- long uncompressedLen, long compressedLen)
-{
- bool result = false;
-
- z_stream zstream;
- int zerr;
- unsigned long compRemaining;
-
- assert(uncompressedLen >= 0);
- assert(compressedLen >= 0);
-
- compRemaining = compressedLen;
-
- /*
- * Initialize the zlib stream.
- */
- memset(&zstream, 0, sizeof(zstream));
- zstream.zalloc = Z_NULL;
- zstream.zfree = Z_NULL;
- zstream.opaque = Z_NULL;
- zstream.next_in = NULL;
- zstream.avail_in = 0;
- zstream.next_out = (Bytef*) buf;
- zstream.avail_out = uncompressedLen;
- zstream.data_type = Z_UNKNOWN;
-
- /*
- * Use the undocumented "negative window bits" feature to tell zlib
- * that there's no zlib header waiting for it.
- */
- zerr = inflateInit2(&zstream, -MAX_WBITS);
- if (zerr != Z_OK) {
- if (zerr == Z_VERSION_ERROR) {
- ALOGE("Installed zlib is not compatible with linked version (%s)\n",
- ZLIB_VERSION);
- } else {
- ALOGE("Call to inflateInit2 failed (zerr=%d)\n", zerr);
- }
- goto bail;
+// TODO: This can go away once the only remaining usage in aapt goes away.
+class FileReader : public zip_archive::Reader {
+ public:
+ FileReader(FILE* fp) : Reader(), mFp(fp), mCurrentOffset(0) {
}
- /*
- * Loop while we have data.
- */
- do {
- unsigned long getSize;
-
- /* read as much as we can */
- if (zstream.avail_in == 0) {
- getSize = (compRemaining > kReadBufSize) ?
- kReadBufSize : compRemaining;
- ALOGV("+++ reading %ld bytes (%ld left)\n",
- getSize, compRemaining);
-
- unsigned char* nextBuffer = NULL;
- const unsigned long nextSize = reader.read(&nextBuffer, getSize);
-
- if (nextSize < getSize || nextBuffer == NULL) {
- ALOGD("inflate read failed (%ld vs %ld)\n", nextSize, getSize);
- goto z_bail;
+ bool ReadAtOffset(uint8_t* buf, size_t len, uint32_t offset) const {
+ // Data is usually requested sequentially, so this helps avoid pointless
+ // fseeks every time we perform a read. There's an impedence mismatch
+ // here because the original API was designed around pread and pwrite.
+ if (offset != mCurrentOffset) {
+ if (fseek(mFp, offset, SEEK_SET) != 0) {
+ return false;
}
- compRemaining -= nextSize;
-
- zstream.next_in = nextBuffer;
- zstream.avail_in = nextSize;
+ mCurrentOffset = offset;
}
- /* uncompress the data */
- zerr = inflate(&zstream, Z_NO_FLUSH);
- if (zerr != Z_OK && zerr != Z_STREAM_END) {
- ALOGD("zlib inflate call failed (zerr=%d)\n", zerr);
- goto z_bail;
+ size_t read = fread(buf, 1, len, mFp);
+ if (read != len) {
+ return false;
}
- /* output buffer holds all, so no need to write the output */
- } while (zerr == Z_OK);
-
- assert(zerr == Z_STREAM_END); /* other errors should've been caught */
-
- if ((long) zstream.total_out != uncompressedLen) {
- ALOGW("Size mismatch on inflated file (%ld vs %ld)\n",
- zstream.total_out, uncompressedLen);
- goto z_bail;
+ mCurrentOffset += read;
+ return true;
}
- // success!
- result = true;
-
-z_bail:
- inflateEnd(&zstream); /* free up any allocated structures */
-
-bail:
- return result;
-}
-
-class FileReader {
-public:
- explicit FileReader(FILE* fp) :
- mFp(fp), mReadBuf(new unsigned char[kReadBufSize])
- {
- }
-
- ~FileReader() {
- delete[] mReadBuf;
- }
-
- long read(unsigned char** nextBuffer, long readSize) const {
- *nextBuffer = mReadBuf;
- return fread(mReadBuf, 1, readSize, mFp);
- }
-
- FILE* mFp;
- unsigned char* mReadBuf;
+ private:
+ FILE* mFp;
+ mutable uint32_t mCurrentOffset;
};
-class FdReader {
-public:
- explicit FdReader(int fd) :
- mFd(fd), mReadBuf(new unsigned char[kReadBufSize])
- {
- }
+class FdReader : public zip_archive::Reader {
+ public:
+ explicit FdReader(int fd) : mFd(fd) {
+ }
- ~FdReader() {
- delete[] mReadBuf;
- }
+ bool ReadAtOffset(uint8_t* buf, size_t len, uint32_t offset) const {
+ return android::base::ReadFullyAtOffset(mFd, buf, len, static_cast<off_t>(offset));
+ }
- long read(unsigned char** nextBuffer, long readSize) const {
- *nextBuffer = mReadBuf;
- return TEMP_FAILURE_RETRY(::read(mFd, mReadBuf, readSize));
- }
-
- int mFd;
- unsigned char* mReadBuf;
+ private:
+ const int mFd;
};
-class BufferReader {
-public:
- BufferReader(void* input, size_t inputSize) :
- mInput(reinterpret_cast<unsigned char*>(input)),
- mInputSize(inputSize),
- mBufferReturned(false)
- {
+class BufferReader : public zip_archive::Reader {
+ public:
+ BufferReader(const void* input, size_t inputSize) : Reader(),
+ mInput(reinterpret_cast<const uint8_t*>(input)),
+ mInputSize(inputSize) {
}
- long read(unsigned char** nextBuffer, long /*readSize*/) {
- if (!mBufferReturned) {
- mBufferReturned = true;
- *nextBuffer = mInput;
- return mInputSize;
+ bool ReadAtOffset(uint8_t* buf, size_t len, uint32_t offset) const {
+ if (offset + len > mInputSize) {
+ return false;
}
- *nextBuffer = NULL;
- return 0;
+ memcpy(buf, mInput + offset, len);
+ return true;
}
- unsigned char* mInput;
+ private:
+ const uint8_t* mInput;
const size_t mInputSize;
- bool mBufferReturned;
+};
+
+class BufferWriter : public zip_archive::Writer {
+ public:
+ BufferWriter(void* output, size_t outputSize) : Writer(),
+ mOutput(reinterpret_cast<uint8_t*>(output)), mOutputSize(outputSize), mBytesWritten(0) {
+ }
+
+ bool Append(uint8_t* buf, size_t bufSize) override {
+ if (mBytesWritten + bufSize > mOutputSize) {
+ return false;
+ }
+
+ memcpy(mOutput + mBytesWritten, buf, bufSize);
+ mBytesWritten += bufSize;
+ return true;
+ }
+
+ private:
+ uint8_t* const mOutput;
+ const size_t mOutputSize;
+ size_t mBytesWritten;
};
/*static*/ bool ZipUtils::inflateToBuffer(FILE* fp, void* buf,
long uncompressedLen, long compressedLen)
{
FileReader reader(fp);
- return ::inflateToBuffer<FileReader>(reader, buf,
- uncompressedLen, compressedLen);
+ BufferWriter writer(buf, uncompressedLen);
+ return (zip_archive::Inflate(reader, compressedLen, uncompressedLen, &writer, nullptr) == 0);
}
/*static*/ bool ZipUtils::inflateToBuffer(int fd, void* buf,
long uncompressedLen, long compressedLen)
{
FdReader reader(fd);
- return ::inflateToBuffer<FdReader>(reader, buf,
- uncompressedLen, compressedLen);
+ BufferWriter writer(buf, uncompressedLen);
+ return (zip_archive::Inflate(reader, compressedLen, uncompressedLen, &writer, nullptr) == 0);
}
-/*static*/ bool ZipUtils::inflateToBuffer(void* in, void* buf,
+/*static*/ bool ZipUtils::inflateToBuffer(const void* in, void* buf,
long uncompressedLen, long compressedLen)
{
BufferReader reader(in, compressedLen);
- return ::inflateToBuffer<BufferReader>(reader, buf,
- uncompressedLen, compressedLen);
+ BufferWriter writer(buf, uncompressedLen);
+ return (zip_archive::Inflate(reader, compressedLen, uncompressedLen, &writer, nullptr) == 0);
}
-
+static inline unsigned long get4LE(const unsigned char* buf) {
+ return buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24);
+}
/*
* Look at the contents of a gzip archive. We want to know where the
@@ -275,7 +186,7 @@
/* quick sanity checks */
if (method == EOF || flags == EOF)
return false;
- if (method != ZipFileRO::kCompressDeflated)
+ if (method != kCompressDeflated)
return false;
/* skip over 4 bytes of mod time, 1 byte XFL, 1 byte OS */
diff --git a/libs/androidfw/include/androidfw/ZipUtils.h b/libs/androidfw/include/androidfw/ZipUtils.h
index 55575d7..4d35e99 100644
--- a/libs/androidfw/include/androidfw/ZipUtils.h
+++ b/libs/androidfw/include/androidfw/ZipUtils.h
@@ -40,7 +40,7 @@
long compressedLen);
static bool inflateToBuffer(int fd, void* buf, long uncompressedLen,
long compressedLen);
- static bool inflateToBuffer(void *in, void* buf, long uncompressedLen,
+ static bool inflateToBuffer(const void *in, void* buf, long uncompressedLen,
long compressedLen);
/*
diff --git a/obex/javax/obex/ObexHelper.java b/obex/javax/obex/ObexHelper.java
index fa50943..478297f 100644
--- a/obex/javax/obex/ObexHelper.java
+++ b/obex/javax/obex/ObexHelper.java
@@ -80,6 +80,9 @@
// The minimum allowed max packet size is 255 according to the OBEX specification
public static final int LOWER_LIMIT_MAX_PACKET_SIZE = 255;
+ // The length of OBEX Byte Sequency Header Id according to the OBEX specification
+ public static final int OBEX_BYTE_SEQ_HEADER_LEN = 0x03;
+
/**
* Temporary workaround to be able to push files to Windows 7.
* TODO: Should be removed as soon as Microsoft updates their driver.
@@ -205,12 +208,15 @@
case 0x40:
boolean trimTail = true;
index++;
- length = 0xFF & headerArray[index];
- length = length << 8;
- index++;
- length += 0xFF & headerArray[index];
- length -= 3;
- index++;
+ length = ((0xFF & headerArray[index]) << 8) +
+ (0xFF & headerArray[index + 1]);
+ index += 2;
+ if (length <= OBEX_BYTE_SEQ_HEADER_LEN) {
+ Log.e(TAG, "Remote sent an OBEX packet with " +
+ "incorrect header length = " + length);
+ break;
+ }
+ length -= OBEX_BYTE_SEQ_HEADER_LEN;
value = new byte[length];
System.arraycopy(headerArray, index, value, 0, length);
if (length == 0 || (length > 0 && (value[length - 1] != 0))) {
diff --git a/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java b/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java
index 9347877..3800e6f 100644
--- a/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java
+++ b/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java
@@ -213,27 +213,6 @@
}
@Override
- public long calculateDirectorySize(String path) throws RemoteException {
- Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
-
- final File dir = Environment.maybeTranslateEmulatedPathToInternal(new File(path));
- if (dir.exists() && dir.isDirectory()) {
- final String targetPath = dir.getAbsolutePath();
- return MeasurementUtils.measureDirectory(targetPath);
- } else {
- return 0L;
- }
- }
-
- @Override
- public long[] getFileSystemStats(String path) {
- Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
-
- final File file = new File(path);
- return new long[] { file.getTotalSpace(), file.getUsableSpace() };
- }
-
- @Override
public void clearDirectory(String path) throws RemoteException {
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index e981e26..a73f800 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -505,11 +505,13 @@
<!-- [CHAR LIMIT=NONE] Label for displaying Bluetooth Audio Codec Parameters while streaming -->
<string name="bluetooth_select_a2dp_codec_streaming_label">Streaming: <xliff:g id="streaming_parameter">%1$s</xliff:g></string>
- <!-- Title of the developer option for DNS over TLS. -->
- <string name="dns_tls">DNS over TLS</string>
- <!-- Summary to explain the developer option for DNS over TLS. This allows the user to
- request that the system attempt TLS with all DNS servers, or none. -->
- <string name="dns_tls_summary">If enabled, attempt DNS over TLS on port 853.</string>
+ <!-- Developer option setting for Private DNS -->
+ <string name="select_private_dns_configuration_title">Private DNS</string>
+ <string name="select_private_dns_configuration_dialog_title">Select Private DNS Mode</string>
+ <string name="private_dns_mode_off">Off</string>
+ <string name="private_dns_mode_opportunistic">Opportunistic</string>
+ <string name="private_dns_mode_provider">Private DNS provider hostname</string>
+ <string name="private_dns_mode_provider_hostname_hint">Enter hostname of DNS provider</string>
<!-- setting Checkbox summary whether to show options for wireless display certification -->
<string name="wifi_display_certification_summary">Show options for wireless display certification</string>
diff --git a/packages/SettingsLib/src/com/android/settingslib/Utils.java b/packages/SettingsLib/src/com/android/settingslib/Utils.java
index dee5a93..e8c4884 100644
--- a/packages/SettingsLib/src/com/android/settingslib/Utils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/Utils.java
@@ -19,6 +19,7 @@
import android.net.ConnectivityManager;
import android.net.NetworkBadging;
import android.os.BatteryManager;
+import android.os.UserHandle;
import android.os.UserManager;
import android.print.PrintManager;
import android.provider.Settings;
@@ -30,6 +31,12 @@
import java.text.NumberFormat;
public class Utils {
+ /** Broadcast intent action when the location mode is about to change. */
+ private static final String MODE_CHANGING_ACTION =
+ "com.android.settings.location.MODE_CHANGING";
+ private static final String CURRENT_MODE_KEY = "CURRENT_MODE";
+ private static final String NEW_MODE_KEY = "NEW_MODE";
+
private static Signature[] sSystemSignature;
private static String sPermissionControllerPackageName;
private static String sServicesSystemSharedLibPackageName;
@@ -43,6 +50,18 @@
com.android.internal.R.drawable.ic_signal_wifi_badged_4_bars
};
+ public static boolean updateLocationMode(Context context, int oldMode, int newMode,
+ int userId) {
+ Intent intent = new Intent(MODE_CHANGING_ACTION);
+ intent.putExtra(CURRENT_MODE_KEY, oldMode);
+ intent.putExtra(NEW_MODE_KEY, newMode);
+ intent.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
+ context.sendBroadcastAsUser(intent, UserHandle.of(userId),
+ android.Manifest.permission.WRITE_SECURE_SETTINGS);
+ return Settings.Secure.putIntForUser(context.getContentResolver(),
+ Settings.Secure.LOCATION_MODE, newMode, userId);
+ }
+
/**
* Return string resource that best describes combination of tethering
* options available on this device.
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/PanProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/PanProfile.java
index 7bda231..3299cb2 100755
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/PanProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/PanProfile.java
@@ -32,7 +32,7 @@
/**
* PanProfile handles Bluetooth PAN profile (NAP and PANU).
*/
-public final class PanProfile implements LocalBluetoothProfile {
+public class PanProfile implements LocalBluetoothProfile {
private static final String TAG = "PanProfile";
private static boolean V = true;
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java
index e2ebbeb..9ac08f9 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java
@@ -51,7 +51,7 @@
@Mock
private A2dpProfile mA2dpProfile;
@Mock
- private HidProfile mHidProfile;
+ private PanProfile mPanProfile;
@Mock
private BluetoothDevice mDevice;
private CachedBluetoothDevice mCachedDevice;
@@ -65,7 +65,7 @@
when(mAdapter.getBluetoothState()).thenReturn(BluetoothAdapter.STATE_ON);
when(mHfpProfile.isProfileReady()).thenReturn(true);
when(mA2dpProfile.isProfileReady()).thenReturn(true);
- when(mHidProfile.isProfileReady()).thenReturn(true);
+ when(mPanProfile.isProfileReady()).thenReturn(true);
mCachedDevice = spy(
new CachedBluetoothDevice(mContext, mAdapter, mProfileManager, mDevice));
doAnswer((invocation) -> mBatteryLevel).when(mCachedDevice).getBatteryLevel();
@@ -83,37 +83,37 @@
@Test
public void testGetConnectionSummary_testSingleProfileConnectDisconnect() {
// Test without battery level
- // Set HID profile to be connected and test connection state summary
- mCachedDevice.onProfileStateChanged(mHidProfile, BluetoothProfile.STATE_CONNECTED);
+ // Set PAN profile to be connected and test connection state summary
+ mCachedDevice.onProfileStateChanged(mPanProfile, BluetoothProfile.STATE_CONNECTED);
assertThat(mCachedDevice.getConnectionSummary()).isEqualTo(mContext.getString(
R.string.bluetooth_connected));
- // Set HID profile to be disconnected and test connection state summary
- mCachedDevice.onProfileStateChanged(mHidProfile, BluetoothProfile.STATE_DISCONNECTED);
+ // Set PAN profile to be disconnected and test connection state summary
+ mCachedDevice.onProfileStateChanged(mPanProfile, BluetoothProfile.STATE_DISCONNECTED);
assertThat(mCachedDevice.getConnectionSummary()).isNull();
// Test with battery level
mBatteryLevel = 10;
- // Set HID profile to be connected and test connection state summary
- mCachedDevice.onProfileStateChanged(mHidProfile, BluetoothProfile.STATE_CONNECTED);
+ // Set PAN profile to be connected and test connection state summary
+ mCachedDevice.onProfileStateChanged(mPanProfile, BluetoothProfile.STATE_CONNECTED);
assertThat(mCachedDevice.getConnectionSummary()).isEqualTo(mContext.getString(
R.string.bluetooth_connected_battery_level,
com.android.settingslib.Utils.formatPercentage(mBatteryLevel)));
- // Set HID profile to be disconnected and test connection state summary
- mCachedDevice.onProfileStateChanged(mHidProfile, BluetoothProfile.STATE_DISCONNECTED);
+ // Set PAN profile to be disconnected and test connection state summary
+ mCachedDevice.onProfileStateChanged(mPanProfile, BluetoothProfile.STATE_DISCONNECTED);
assertThat(mCachedDevice.getConnectionSummary()).isNull();
// Test with BluetoothDevice.BATTERY_LEVEL_UNKNOWN battery level
mBatteryLevel = BluetoothDevice.BATTERY_LEVEL_UNKNOWN;
- // Set HID profile to be connected and test connection state summary
- mCachedDevice.onProfileStateChanged(mHidProfile, BluetoothProfile.STATE_CONNECTED);
+ // Set PAN profile to be connected and test connection state summary
+ mCachedDevice.onProfileStateChanged(mPanProfile, BluetoothProfile.STATE_CONNECTED);
assertThat(mCachedDevice.getConnectionSummary()).isEqualTo(mContext.getString(
R.string.bluetooth_connected));
- // Set HID profile to be disconnected and test connection state summary
- mCachedDevice.onProfileStateChanged(mHidProfile, BluetoothProfile.STATE_DISCONNECTED);
+ // Set PAN profile to be disconnected and test connection state summary
+ mCachedDevice.onProfileStateChanged(mPanProfile, BluetoothProfile.STATE_DISCONNECTED);
assertThat(mCachedDevice.getConnectionSummary()).isNull();
}
@@ -121,10 +121,10 @@
public void testGetConnectionSummary_testMultipleProfileConnectDisconnect() {
mBatteryLevel = 10;
- // Set HFP, A2DP and HID profile to be connected and test connection state summary
+ // Set HFP, A2DP and PAN profile to be connected and test connection state summary
mCachedDevice.onProfileStateChanged(mHfpProfile, BluetoothProfile.STATE_CONNECTED);
mCachedDevice.onProfileStateChanged(mA2dpProfile, BluetoothProfile.STATE_CONNECTED);
- mCachedDevice.onProfileStateChanged(mHidProfile, BluetoothProfile.STATE_CONNECTED);
+ mCachedDevice.onProfileStateChanged(mPanProfile, BluetoothProfile.STATE_CONNECTED);
assertThat(mCachedDevice.getConnectionSummary()).isEqualTo(mContext.getString(
R.string.bluetooth_connected_battery_level,
com.android.settingslib.Utils.formatPercentage(mBatteryLevel)));
@@ -149,7 +149,7 @@
com.android.settingslib.Utils.formatPercentage(mBatteryLevel)));
// Disconnect all profiles and test connection state summary
- mCachedDevice.onProfileStateChanged(mHidProfile, BluetoothProfile.STATE_DISCONNECTED);
+ mCachedDevice.onProfileStateChanged(mPanProfile, BluetoothProfile.STATE_DISCONNECTED);
assertThat(mCachedDevice.getConnectionSummary()).isNull();
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationControllerImpl.java
index 874f0d9..6d6aa52 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/LocationControllerImpl.java
@@ -39,6 +39,8 @@
import java.util.ArrayList;
import java.util.List;
+import static com.android.settingslib.Utils.updateLocationMode;
+
/**
* A controller to manage changes of location related states and update the views accordingly.
*/
@@ -106,12 +108,13 @@
final ContentResolver cr = mContext.getContentResolver();
// When enabling location, a user consent dialog will pop up, and the
// setting won't be fully enabled until the user accepts the agreement.
+ int currentMode = Settings.Secure.getIntForUser(cr, Settings.Secure.LOCATION_MODE,
+ Settings.Secure.LOCATION_MODE_OFF, currentUserId);
int mode = enabled
? Settings.Secure.LOCATION_MODE_PREVIOUS : Settings.Secure.LOCATION_MODE_OFF;
// QuickSettings always runs as the owner, so specifically set the settings
// for the current foreground user.
- return Settings.Secure
- .putIntForUser(cr, Settings.Secure.LOCATION_MODE, mode, currentUserId);
+ return updateLocationMode(mContext, currentMode, mode, currentUserId);
}
/**
diff --git a/proto/src/ipconnectivity.proto b/proto/src/ipconnectivity.proto
index 8dd35af..82a3415 100644
--- a/proto/src/ipconnectivity.proto
+++ b/proto/src/ipconnectivity.proto
@@ -50,9 +50,10 @@
optional int32 value = 2;
};
-// An event record when the system default network disconnects or the system
-// switches to a new default network.
-// Next tag: 10.
+// An event recorded when the system default network disconnects or the system
+// switches to a new default network. An event is also recorded to cover gaps
+// without a default network.
+// Next tag: 12
message DefaultNetworkEvent {
// Reason why this network stopped being the default.
@@ -72,26 +73,34 @@
};
// Duration in milliseconds when this network was the default.
- // Since version 4
+ // Since P.
optional int64 default_network_duration_ms = 5;
- // Duration in milliseconds without a default network before this network
- // became the default.
- // Since version 4
- optional int64 no_default_network_duration_ms = 6;
+ // Duration in milliseconds when this default network Internet access was
+ // validated. This field is equal to 0 for DefaultNetworkEvents representing
+ // lack of a default network.
+ // Since P.
+ optional int64 validation_duration_ms = 11;
// Network score of this network when it became the default network.
- // Since version 4
+ // Or 0 if this event represents a period without a default network.
+ // Since P.
optional int64 initial_score = 7;
// Network score of this network when it stopped being the default network.
- // Since version 4
+ // Or 0 if this event represents a period without a default network.
+ // Since P.
optional int64 final_score = 8;
// Best available information about IP support of this default network.
- // Since version 4
+ // Or NONE if this event represents a period without a default network.
+ // Since P.
optional IPSupport ip_support = 9;
+ // LinkLayer of the previous default network. Ignores any previous period
+ // without a default network.
+ // Since P
+ optional LinkLayer previous_default_network_link_layer = 10;
// Deprecated fields
@@ -112,6 +121,11 @@
// TRANSPORT_* constants as defined in NetworkCapabilities.
// Deprecated since version 3. Replaced by top-level transports field.
repeated int32 transport_types = 4 [deprecated = true];
+
+ // Duration in milliseconds without a default network. This field is non-zero
+ // only for DefaultNetworkEvents representing lack of a default network.
+ // Since P.
+ optional int64 no_default_network_duration_ms = 6 [deprecated = true];
};
// Logs IpReachabilityMonitor probe events and NUD_FAILED events.
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 25c96d1..664c2ab 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -29,7 +29,10 @@
import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING;
import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED;
+import static android.net.NetworkCapabilities.TRANSPORT_VPN;
+
import static com.android.internal.util.Preconditions.checkNotNull;
import android.annotation.Nullable;
@@ -71,6 +74,7 @@
import android.net.RouteInfo;
import android.net.UidRange;
import android.net.Uri;
+import android.net.VpnService;
import android.net.metrics.IpConnectivityLog;
import android.net.metrics.NetworkEvent;
import android.net.util.MultinetworkPolicyTracker;
@@ -2109,9 +2113,14 @@
final boolean valid =
(msg.arg1 == NetworkMonitor.NETWORK_TEST_RESULT_VALID);
final boolean wasValidated = nai.lastValidated;
+ final boolean wasDefault = isDefaultNetwork(nai);
if (DBG) log(nai.name() + " validation " + (valid ? "passed" : "failed") +
(msg.obj == null ? "" : " with redirect to " + (String)msg.obj));
if (valid != nai.lastValidated) {
+ if (wasDefault) {
+ metricsLogger().defaultNetworkMetrics().logDefaultNetworkValidity(
+ SystemClock.elapsedRealtime(), valid);
+ }
final int oldScore = nai.getCurrentScore();
nai.lastValidated = valid;
nai.everValidated |= valid;
@@ -2283,7 +2292,8 @@
// Let rematchAllNetworksAndRequests() below record a new default network event
// if there is a fallback. Taken together, the two form a X -> 0, 0 -> Y sequence
// whose timestamps tell how long it takes to recover a default network.
- metricsLogger().defaultNetworkMetrics().logDefaultNetworkEvent(null, nai);
+ long now = SystemClock.elapsedRealtime();
+ metricsLogger().defaultNetworkMetrics().logDefaultNetworkEvent(now, null, nai);
}
notifyIfacesChangedForNetworkStats();
// TODO - we shouldn't send CALLBACK_LOST to requests that can be satisfied
@@ -4664,10 +4674,12 @@
}
}
- final NetworkCapabilities prevNc = nai.networkCapabilities;
+ final NetworkCapabilities prevNc;
synchronized (nai) {
+ prevNc = nai.networkCapabilities;
nai.networkCapabilities = networkCapabilities;
}
+
if (nai.getCurrentScore() == oldScore &&
networkCapabilities.equalRequestableCapabilities(prevNc)) {
// If the requestable capabilities haven't changed, and the score hasn't changed, then
@@ -4681,6 +4693,28 @@
rematchAllNetworksAndRequests(nai, oldScore);
notifyNetworkCallbacks(nai, ConnectivityManager.CALLBACK_CAP_CHANGED);
}
+
+ // Report changes that are interesting for network statistics tracking.
+ if (prevNc != null) {
+ final boolean meteredChanged = prevNc.hasCapability(NET_CAPABILITY_NOT_METERED) !=
+ networkCapabilities.hasCapability(NET_CAPABILITY_NOT_METERED);
+ final boolean roamingChanged = prevNc.hasCapability(NET_CAPABILITY_NOT_ROAMING) !=
+ networkCapabilities.hasCapability(NET_CAPABILITY_NOT_ROAMING);
+ if (meteredChanged || roamingChanged) {
+ notifyIfacesChangedForNetworkStats();
+ }
+ }
+
+ if (!networkCapabilities.hasTransport(TRANSPORT_VPN)) {
+ // Tell VPNs about updated capabilities, since they may need to
+ // bubble those changes through.
+ synchronized (mVpns) {
+ for (int i = 0; i < mVpns.size(); i++) {
+ final Vpn vpn = mVpns.valueAt(i);
+ vpn.updateCapabilities();
+ }
+ }
+ }
}
public void handleUpdateLinkProperties(NetworkAgentInfo nai, LinkProperties newLp) {
@@ -5013,7 +5047,7 @@
makeDefault(newNetwork);
// Log 0 -> X and Y -> X default network transitions, where X is the new default.
metricsLogger().defaultNetworkMetrics().logDefaultNetworkEvent(
- newNetwork, oldDefaultNetwork);
+ now, newNetwork, oldDefaultNetwork);
// Have a new default network, release the transition wakelock in
scheduleReleaseNetworkTransitionWakelock();
}
@@ -5214,14 +5248,6 @@
}
notifyLockdownVpn(networkAgent);
- if (oldInfo != null && oldInfo.getState() == state) {
- if (oldInfo.isRoaming() != newInfo.isRoaming()) {
- if (VDBG) log("roaming status changed, notifying NetworkStatsService");
- notifyIfacesChangedForNetworkStats();
- } else if (VDBG) log("ignoring duplicate network state non-change");
- // In either case, no further work should be needed.
- return;
- }
if (DBG) {
log(networkAgent.name() + " EVENT_NETWORK_INFO_CHANGED, going from " +
(oldInfo == null ? "null" : oldInfo.getState()) +
diff --git a/services/core/java/com/android/server/NetworkManagementService.java b/services/core/java/com/android/server/NetworkManagementService.java
index b1d6f73..7c73818 100644
--- a/services/core/java/com/android/server/NetworkManagementService.java
+++ b/services/core/java/com/android/server/NetworkManagementService.java
@@ -20,6 +20,9 @@
import static android.Manifest.permission.DUMP;
import static android.Manifest.permission.NETWORK_STACK;
import static android.Manifest.permission.SHUTDOWN;
+import static android.net.ConnectivityManager.PRIVATE_DNS_DEFAULT_MODE;
+import static android.net.ConnectivityManager.PRIVATE_DNS_MODE_OPPORTUNISTIC;
+import static android.net.ConnectivityManager.PRIVATE_DNS_MODE_PROVIDER_HOSTNAME;
import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_DOZABLE;
import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_DOZABLE;
import static android.net.NetworkPolicyManager.FIREWALL_CHAIN_NAME_NONE;
@@ -92,6 +95,7 @@
import android.telephony.PhoneStateListener;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
+import android.text.TextUtils;
import android.util.Log;
import android.util.Slog;
import android.util.SparseBooleanArray;
@@ -1946,9 +1950,9 @@
public void setDnsConfigurationForNetwork(int netId, String[] servers, String domains) {
mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
- ContentResolver resolver = mContext.getContentResolver();
+ final ContentResolver cr = mContext.getContentResolver();
- int sampleValidity = Settings.Global.getInt(resolver,
+ int sampleValidity = Settings.Global.getInt(cr,
Settings.Global.DNS_RESOLVER_SAMPLE_VALIDITY_SECONDS,
DNS_RESOLVER_DEFAULT_SAMPLE_VALIDITY_SECONDS);
if (sampleValidity < 0 || sampleValidity > 65535) {
@@ -1957,7 +1961,7 @@
sampleValidity = DNS_RESOLVER_DEFAULT_SAMPLE_VALIDITY_SECONDS;
}
- int successThreshold = Settings.Global.getInt(resolver,
+ int successThreshold = Settings.Global.getInt(cr,
Settings.Global.DNS_RESOLVER_SUCCESS_THRESHOLD_PERCENT,
DNS_RESOLVER_DEFAULT_SUCCESS_THRESHOLD_PERCENT);
if (successThreshold < 0 || successThreshold > 100) {
@@ -1966,9 +1970,9 @@
successThreshold = DNS_RESOLVER_DEFAULT_SUCCESS_THRESHOLD_PERCENT;
}
- int minSamples = Settings.Global.getInt(resolver,
+ int minSamples = Settings.Global.getInt(cr,
Settings.Global.DNS_RESOLVER_MIN_SAMPLES, DNS_RESOLVER_DEFAULT_MIN_SAMPLES);
- int maxSamples = Settings.Global.getInt(resolver,
+ int maxSamples = Settings.Global.getInt(cr,
Settings.Global.DNS_RESOLVER_MAX_SAMPLES, DNS_RESOLVER_DEFAULT_MAX_SAMPLES);
if (minSamples < 0 || minSamples > maxSamples || maxSamples > 64) {
Slog.w(TAG, "Invalid sample count (min, max)=(" + minSamples + ", " + maxSamples +
@@ -1980,8 +1984,24 @@
final String[] domainStrs = domains == null ? new String[0] : domains.split(" ");
final int[] params = { sampleValidity, successThreshold, minSamples, maxSamples };
- final boolean useTls = Settings.Global.getInt(resolver,
- Settings.Global.DNS_TLS_DISABLED, 0) == 0;
+ final boolean useTls = shouldUseTls(cr);
+ // TODO: Populate tlsHostname once it's decided how the hostname's IP
+ // addresses will be resolved:
+ //
+ // [1] network-provided DNS servers are included here with the
+ // hostname and netd will use the network-provided servers to
+ // resolve the hostname and fix up its internal structures, or
+ //
+ // [2] network-provided DNS servers are included here without the
+ // hostname, the ConnectivityService layer resolves the given
+ // hostname, and then reconfigures netd with this information.
+ //
+ // In practice, there will always be a need for ConnectivityService or
+ // the captive portal app to use the network-provided services to make
+ // some queries. This argues in favor of [1], in concert with another
+ // mechanism, perhaps setting a high bit in the netid, to indicate
+ // via existing DNS APIs which set of servers (network-provided or
+ // non-network-provided private DNS) should be queried.
final String tlsHostname = "";
final String[] tlsFingerprints = new String[0];
try {
@@ -1992,6 +2012,15 @@
}
}
+ private static boolean shouldUseTls(ContentResolver cr) {
+ String privateDns = Settings.Global.getString(cr, Settings.Global.PRIVATE_DNS_MODE);
+ if (TextUtils.isEmpty(privateDns)) {
+ privateDns = PRIVATE_DNS_DEFAULT_MODE;
+ }
+ return privateDns.equals(PRIVATE_DNS_MODE_OPPORTUNISTIC) ||
+ privateDns.startsWith(PRIVATE_DNS_MODE_PROVIDER_HOSTNAME);
+ }
+
@Override
public void addVpnUidRanges(int netId, UidRange[] ranges) {
mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG);
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index e609bd7..831c9cb 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -1356,31 +1356,6 @@
}
}
- public void notifyOemHookRawEventForSubscriber(int subId, byte[] rawData) {
- if (!checkNotifyPermission("notifyOemHookRawEventForSubscriber")) {
- return;
- }
-
- synchronized (mRecords) {
- for (Record r : mRecords) {
- if (VDBG) {
- log("notifyOemHookRawEventForSubscriber: r=" + r + " subId=" + subId);
- }
- if ((r.matchPhoneStateListenerEvent(
- PhoneStateListener.LISTEN_OEM_HOOK_RAW_EVENT)) &&
- ((r.subId == subId) ||
- (r.subId == SubscriptionManager.DEFAULT_SUBSCRIPTION_ID))) {
- try {
- r.callback.onOemHookRawEvent(rawData);
- } catch (RemoteException ex) {
- mRemoveList.add(r.binder);
- }
- }
- }
- handleRemoveListLocked();
- }
- }
-
@Override
public void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
final IndentingPrintWriter pw = new IndentingPrintWriter(writer, " ");
@@ -1673,11 +1648,6 @@
android.Manifest.permission.READ_PRECISE_PHONE_STATE, null);
}
-
- if ((events & PhoneStateListener.LISTEN_OEM_HOOK_RAW_EVENT) != 0) {
- mContext.enforceCallingOrSelfPermission(
- android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, null);
- }
}
private void handleRemoveListLocked() {
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index d9944a7..90b8e29 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -3857,7 +3857,7 @@
}
if (app.info.isPrivilegedApp() &&
- !SystemProperties.getBoolean("pm.dexopt.priv-apps", true)) {
+ SystemProperties.getBoolean("pm.dexopt.priv-apps-oob", false)) {
runtimeFlags |= Zygote.DISABLE_VERIFIER;
runtimeFlags |= Zygote.ONLY_USE_SYSTEM_OAT_FILES;
}
diff --git a/services/core/java/com/android/server/connectivity/DefaultNetworkMetrics.java b/services/core/java/com/android/server/connectivity/DefaultNetworkMetrics.java
index 8981db1..28c3585 100644
--- a/services/core/java/com/android/server/connectivity/DefaultNetworkMetrics.java
+++ b/services/core/java/com/android/server/connectivity/DefaultNetworkMetrics.java
@@ -18,9 +18,11 @@
import android.net.LinkProperties;
import android.net.metrics.DefaultNetworkEvent;
-import android.net.metrics.IpConnectivityLog;
+import android.os.SystemClock;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.BitUtils;
+import com.android.internal.util.RingBuffer;
import com.android.server.connectivity.metrics.nano.IpConnectivityLogClass.IpConnectivityEvent;
import java.io.PrintWriter;
@@ -35,19 +37,49 @@
private static final int ROLLING_LOG_SIZE = 64;
+ public final long creationTimeMs = SystemClock.elapsedRealtime();
+
// Event buffer used for metrics upload. The buffer is cleared when events are collected.
@GuardedBy("this")
private final List<DefaultNetworkEvent> mEvents = new ArrayList<>();
+ // Rolling event buffer used for dumpsys and bugreports.
+ @GuardedBy("this")
+ private final RingBuffer<DefaultNetworkEvent> mEventsLog =
+ new RingBuffer(DefaultNetworkEvent.class, ROLLING_LOG_SIZE);
+
+ // Information about the current status of the default network.
+ @GuardedBy("this")
+ private DefaultNetworkEvent mCurrentDefaultNetwork;
+ @GuardedBy("this")
+ private boolean mIsCurrentlyValid;
+ @GuardedBy("this")
+ private long mLastValidationTimeMs;
+ // Transport information about the last default network.
+ @GuardedBy("this")
+ private int mLastTransports;
+
+ public DefaultNetworkMetrics() {
+ newDefaultNetwork(creationTimeMs, null);
+ }
+
public synchronized void listEvents(PrintWriter pw) {
+ pw.println("default network events:");
long localTimeMs = System.currentTimeMillis();
- for (DefaultNetworkEvent ev : mEvents) {
- pw.println(ev);
+ long timeMs = SystemClock.elapsedRealtime();
+ for (DefaultNetworkEvent ev : mEventsLog.toArray()) {
+ printEvent(localTimeMs, pw, ev);
}
+ mCurrentDefaultNetwork.updateDuration(timeMs);
+ if (mIsCurrentlyValid) {
+ updateValidationTime(timeMs);
+ mLastValidationTimeMs = timeMs;
+ }
+ printEvent(localTimeMs, pw, mCurrentDefaultNetwork);
}
public synchronized void listEventsAsProto(PrintWriter pw) {
- for (DefaultNetworkEvent ev : mEvents) {
+ for (DefaultNetworkEvent ev : mEventsLog.toArray()) {
pw.print(IpConnectivityEventBuilder.toProto(ev));
}
}
@@ -59,20 +91,75 @@
mEvents.clear();
}
- public synchronized void logDefaultNetworkEvent(
- NetworkAgentInfo newNai, NetworkAgentInfo prevNai) {
- DefaultNetworkEvent ev = new DefaultNetworkEvent();
- if (newNai != null) {
- ev.netId = newNai.network().netId;
- ev.transportTypes = newNai.networkCapabilities.getTransportTypes();
- }
- if (prevNai != null) {
- ev.prevNetId = prevNai.network().netId;
- final LinkProperties lp = prevNai.linkProperties;
- ev.prevIPv4 = lp.hasIPv4Address() && lp.hasIPv4DefaultRoute();
- ev.prevIPv6 = lp.hasGlobalIPv6Address() && lp.hasIPv6DefaultRoute();
+ public synchronized void logDefaultNetworkValidity(long timeMs, boolean isValid) {
+ if (!isValid && mIsCurrentlyValid) {
+ mIsCurrentlyValid = false;
+ updateValidationTime(timeMs);
}
+ if (isValid && !mIsCurrentlyValid) {
+ mIsCurrentlyValid = true;
+ mLastValidationTimeMs = timeMs;
+ }
+ }
+
+ private void updateValidationTime(long timeMs) {
+ mCurrentDefaultNetwork.validatedMs += timeMs - mLastValidationTimeMs;
+ }
+
+ public synchronized void logDefaultNetworkEvent(
+ long timeMs, NetworkAgentInfo newNai, NetworkAgentInfo oldNai) {
+ logCurrentDefaultNetwork(timeMs, oldNai);
+ newDefaultNetwork(timeMs, newNai);
+ }
+
+ private void logCurrentDefaultNetwork(long timeMs, NetworkAgentInfo oldNai) {
+ DefaultNetworkEvent ev = mCurrentDefaultNetwork;
+ ev.updateDuration(timeMs);
+ ev.previousTransports = mLastTransports;
+ // oldNai is null if the system had no default network before the transition.
+ if (oldNai != null) {
+ // The system acquired a new default network.
+ fillLinkInfo(ev, oldNai);
+ ev.finalScore = oldNai.getCurrentScore();
+ ev.validatedMs = ev.durationMs;
+ }
+ // Only change transport of the previous default network if the event currently logged
+ // corresponds to an existing default network, and not to the absence of a default network.
+ // This allows to log pairs of transports for successive default networks regardless of
+ // whether or not the system experienced a period without any default network.
+ if (ev.transports != 0) {
+ mLastTransports = ev.transports;
+ }
mEvents.add(ev);
+ mEventsLog.append(ev);
+ }
+
+ private void newDefaultNetwork(long timeMs, NetworkAgentInfo newNai) {
+ DefaultNetworkEvent ev = new DefaultNetworkEvent(timeMs);
+ ev.durationMs = timeMs;
+ // newNai is null if the system has no default network after the transition.
+ if (newNai != null) {
+ fillLinkInfo(ev, newNai);
+ ev.initialScore = newNai.getCurrentScore();
+ if (newNai.lastValidated) {
+ mIsCurrentlyValid = true;
+ mLastValidationTimeMs = timeMs;
+ }
+ }
+ mCurrentDefaultNetwork = ev;
+ }
+
+ private static void fillLinkInfo(DefaultNetworkEvent ev, NetworkAgentInfo nai) {
+ LinkProperties lp = nai.linkProperties;
+ ev.netId = nai.network().netId;
+ ev.transports |= BitUtils.packBits(nai.networkCapabilities.getTransportTypes());
+ ev.ipv4 |= lp.hasIPv4Address() && lp.hasIPv4DefaultRoute();
+ ev.ipv6 |= lp.hasGlobalIPv6Address() && lp.hasIPv6DefaultRoute();
+ }
+
+ private static void printEvent(long localTimeMs, PrintWriter pw, DefaultNetworkEvent ev) {
+ long localCreationTimeMs = localTimeMs - ev.durationMs;
+ pw.println(String.format("%tT.%tL: %s", localCreationTimeMs, localCreationTimeMs, ev));
}
}
diff --git a/services/core/java/com/android/server/connectivity/IpConnectivityEventBuilder.java b/services/core/java/com/android/server/connectivity/IpConnectivityEventBuilder.java
index 3d71ecb..a011692b 100644
--- a/services/core/java/com/android/server/connectivity/IpConnectivityEventBuilder.java
+++ b/services/core/java/com/android/server/connectivity/IpConnectivityEventBuilder.java
@@ -25,6 +25,7 @@
import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
import static android.net.NetworkCapabilities.TRANSPORT_WIFI_AWARE;
+import android.net.ConnectivityManager;
import android.net.ConnectivityMetricsEvent;
import android.net.metrics.ApfProgramEvent;
import android.net.metrics.ApfStats;
@@ -135,11 +136,17 @@
public static IpConnectivityEvent toProto(DefaultNetworkEvent in) {
IpConnectivityLogClass.DefaultNetworkEvent ev =
new IpConnectivityLogClass.DefaultNetworkEvent();
- ev.networkId = netIdOf(in.netId);
- ev.previousNetworkId = netIdOf(in.prevNetId);
- ev.transportTypes = in.transportTypes;
- ev.previousNetworkIpSupport = ipSupportOf(in);
- final IpConnectivityEvent out = buildEvent(in.netId, 0, null);
+ ev.finalScore = in.finalScore;
+ ev.initialScore = in.initialScore;
+ ev.ipSupport = ipSupportOf(in);
+ ev.defaultNetworkDurationMs = in.durationMs;
+ ev.validationDurationMs = in.validatedMs;
+ ev.previousDefaultNetworkLinkLayer = transportsToLinkLayer(in.previousTransports);
+ final IpConnectivityEvent out = buildEvent(in.netId, in.transports, null);
+ if (in.transports == 0) {
+ // Set link layer to NONE for events representing the absence of a default network.
+ out.linkLayer = IpConnectivityLogClass.NONE;
+ }
out.setDefaultNetworkEvent(ev);
return out;
}
@@ -321,13 +328,13 @@
}
private static int ipSupportOf(DefaultNetworkEvent in) {
- if (in.prevIPv4 && in.prevIPv6) {
+ if (in.ipv4 && in.ipv6) {
return IpConnectivityLogClass.DefaultNetworkEvent.DUAL;
}
- if (in.prevIPv6) {
+ if (in.ipv6) {
return IpConnectivityLogClass.DefaultNetworkEvent.IPV6;
}
- if (in.prevIPv4) {
+ if (in.ipv4) {
return IpConnectivityLogClass.DefaultNetworkEvent.IPV4;
}
return IpConnectivityLogClass.DefaultNetworkEvent.NONE;
diff --git a/services/core/java/com/android/server/connectivity/IpConnectivityMetrics.java b/services/core/java/com/android/server/connectivity/IpConnectivityMetrics.java
index 24217e6..5cc390a 100644
--- a/services/core/java/com/android/server/connectivity/IpConnectivityMetrics.java
+++ b/services/core/java/com/android/server/connectivity/IpConnectivityMetrics.java
@@ -45,6 +45,7 @@
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.List;
import java.util.function.ToIntFunction;
@@ -214,86 +215,66 @@
}
/**
- * Clears the event buffer and prints its content as a protobuf serialized byte array
+ * Clear the event buffer and prints its content as a protobuf serialized byte array
* inside a base64 encoded string.
*/
- private void cmdFlush(FileDescriptor fd, PrintWriter pw, String[] args) {
+ private void cmdFlush(PrintWriter pw) {
pw.print(flushEncodedOutput());
}
/**
- * Prints the content of the event buffer, either using the events ASCII representation
- * or using protobuf text format.
+ * Print the content of the rolling event buffer in human readable format.
+ * Also print network dns/connect statistics and recent default network events.
*/
- private void cmdList(FileDescriptor fd, PrintWriter pw, String[] args) {
- final ArrayList<ConnectivityMetricsEvent> events;
- synchronized (mLock) {
- events = new ArrayList(mBuffer);
- }
-
- if (args.length > 1 && args[1].equals("proto")) {
- for (IpConnectivityEvent ev : IpConnectivityEventBuilder.toProto(events)) {
- pw.print(ev.toString());
- }
- if (mNetdListener != null) {
- mNetdListener.listAsProtos(pw);
- }
- mDefaultNetworkMetrics.listEventsAsProto(pw);
- return;
- }
-
+ private void cmdList(PrintWriter pw) {
+ pw.println("metrics events:");
+ final List<ConnectivityMetricsEvent> events = getEvents();
for (ConnectivityMetricsEvent ev : events) {
pw.println(ev.toString());
}
+ pw.println("");
if (mNetdListener != null) {
mNetdListener.list(pw);
}
+ pw.println("");
mDefaultNetworkMetrics.listEvents(pw);
}
- /**
- * Prints for bug reports the content of the rolling event log and the
- * content of Netd event listener.
+ /*
+ * Print the content of the rolling event buffer in text proto format.
*/
- private void cmdDumpsys(FileDescriptor fd, PrintWriter pw, String[] args) {
- final ConnectivityMetricsEvent[] events;
- synchronized (mLock) {
- events = mEventLog.toArray();
- }
- for (ConnectivityMetricsEvent ev : events) {
- pw.println(ev.toString());
+ private void cmdListAsProto(PrintWriter pw) {
+ final List<ConnectivityMetricsEvent> events = getEvents();
+ for (IpConnectivityEvent ev : IpConnectivityEventBuilder.toProto(events)) {
+ pw.print(ev.toString());
}
if (mNetdListener != null) {
- mNetdListener.list(pw);
+ mNetdListener.listAsProtos(pw);
}
- mDefaultNetworkMetrics.listEvents(pw);
+ mDefaultNetworkMetrics.listEventsAsProto(pw);
}
- private void cmdStats(FileDescriptor fd, PrintWriter pw, String[] args) {
+ /*
+ * Return a copy of metrics events stored in buffer for metrics uploading.
+ */
+ private List<ConnectivityMetricsEvent> getEvents() {
synchronized (mLock) {
- pw.println("Buffered events: " + mBuffer.size());
- pw.println("Buffer capacity: " + mCapacity);
- pw.println("Dropped events: " + mDropped);
+ return Arrays.asList(mEventLog.toArray());
}
- if (mNetdListener != null) {
- mNetdListener.dump(pw);
- }
- }
-
- private void cmdDefault(FileDescriptor fd, PrintWriter pw, String[] args) {
- if (args.length == 0) {
- pw.println("No command");
- return;
- }
- pw.println("Unknown command " + TextUtils.join(" ", args));
}
public final class Impl extends IIpConnectivityMetrics.Stub {
- static final String CMD_FLUSH = "flush";
- static final String CMD_LIST = "list";
- static final String CMD_STATS = "stats";
- static final String CMD_DUMPSYS = "-a"; // dumpsys.cpp dumps services with "-a" as arguments
- static final String CMD_DEFAULT = CMD_STATS;
+ // Dump and flushes the metrics event buffer in base64 encoded serialized proto output.
+ static final String CMD_FLUSH = "flush";
+ // Dump the rolling buffer of metrics event in human readable proto text format.
+ static final String CMD_PROTO = "proto";
+ // Dump the rolling buffer of metrics event and pretty print events using a human readable
+ // format. Also print network dns/connect statistics and default network event time series.
+ static final String CMD_LIST = "list";
+ // By default any other argument will fall into the default case which is remapped to the
+ // "list" command. This includes most notably bug reports collected by dumpsys.cpp with
+ // the "-a" argument.
+ static final String CMD_DEFAULT = CMD_LIST;
@Override
public int logEvent(ConnectivityMetricsEvent event) {
@@ -308,19 +289,15 @@
final String cmd = (args.length > 0) ? args[0] : CMD_DEFAULT;
switch (cmd) {
case CMD_FLUSH:
- cmdFlush(fd, pw, args);
+ cmdFlush(pw);
return;
- case CMD_DUMPSYS:
- cmdDumpsys(fd, pw, args);
+ case CMD_PROTO:
+ cmdListAsProto(pw);
return;
- case CMD_LIST:
- cmdList(fd, pw, args);
- return;
- case CMD_STATS:
- cmdStats(fd, pw, args);
- return;
+ case CMD_LIST: // fallthrough
default:
- cmdDefault(fd, pw, args);
+ cmdList(pw);
+ return;
}
}
diff --git a/services/core/java/com/android/server/connectivity/NetdEventListenerService.java b/services/core/java/com/android/server/connectivity/NetdEventListenerService.java
index 05c6e69..61b11e1 100644
--- a/services/core/java/com/android/server/connectivity/NetdEventListenerService.java
+++ b/services/core/java/com/android/server/connectivity/NetdEventListenerService.java
@@ -243,24 +243,21 @@
mWakeupStats.clear();
}
- public synchronized void dump(PrintWriter writer) {
- IndentingPrintWriter pw = new IndentingPrintWriter(writer, " ");
- pw.println(TAG + ":");
- pw.increaseIndent();
- list(pw);
- pw.decreaseIndent();
- }
-
public synchronized void list(PrintWriter pw) {
+ pw.println("dns/connect events:");
for (int i = 0; i < mNetworkMetrics.size(); i++) {
pw.println(mNetworkMetrics.valueAt(i).connectMetrics);
}
for (int i = 0; i < mNetworkMetrics.size(); i++) {
pw.println(mNetworkMetrics.valueAt(i).dnsMetrics);
}
+ pw.println("");
+ pw.println("network statistics:");
for (NetworkMetricsSnapshot s : getNetworkMetricsSnapshots()) {
pw.println(s);
}
+ pw.println("");
+ pw.println("packet wakeup events:");
for (int i = 0; i < mWakeupStats.size(); i++) {
pw.println(mWakeupStats.valueAt(i));
}
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index a44b18d..7715727 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -18,6 +18,8 @@
import static android.Manifest.permission.BIND_VPN_SERVICE;
import static android.net.ConnectivityManager.NETID_UNSET;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING;
import static android.net.RouteInfo.RTN_THROW;
import static android.net.RouteInfo.RTN_UNREACHABLE;
@@ -90,6 +92,8 @@
import com.android.internal.net.VpnInfo;
import com.android.internal.net.VpnProfile;
import com.android.internal.notification.SystemNotificationChannels;
+import com.android.internal.util.ArrayUtils;
+import com.android.server.ConnectivityService;
import com.android.server.DeviceIdleController;
import com.android.server.LocalServices;
import com.android.server.net.BaseNetworkObserver;
@@ -245,10 +249,10 @@
}
mNetworkInfo = new NetworkInfo(ConnectivityManager.TYPE_VPN, 0, NETWORKTYPE, "");
- // TODO: Copy metered attribute and bandwidths from physical transport, b/16207332
mNetworkCapabilities = new NetworkCapabilities();
mNetworkCapabilities.addTransportType(NetworkCapabilities.TRANSPORT_VPN);
mNetworkCapabilities.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN);
+ updateCapabilities();
loadAlwaysOnPackage();
}
@@ -275,6 +279,62 @@
updateAlwaysOnNotification(detailedState);
}
+ public void updateCapabilities() {
+ final Network[] underlyingNetworks = (mConfig != null) ? mConfig.underlyingNetworks : null;
+ updateCapabilities(mContext.getSystemService(ConnectivityManager.class), underlyingNetworks,
+ mNetworkCapabilities);
+
+ if (mNetworkAgent != null) {
+ mNetworkAgent.sendNetworkCapabilities(mNetworkCapabilities);
+ }
+ }
+
+ @VisibleForTesting
+ public static void updateCapabilities(ConnectivityManager cm, Network[] underlyingNetworks,
+ NetworkCapabilities caps) {
+ int[] transportTypes = new int[] { NetworkCapabilities.TRANSPORT_VPN };
+ int downKbps = NetworkCapabilities.LINK_BANDWIDTH_UNSPECIFIED;
+ int upKbps = NetworkCapabilities.LINK_BANDWIDTH_UNSPECIFIED;
+ boolean metered = false;
+ boolean roaming = false;
+
+ if (ArrayUtils.isEmpty(underlyingNetworks)) {
+ // No idea what the underlying networks are; assume sane defaults
+ metered = true;
+ roaming = false;
+ } else {
+ for (Network underlying : underlyingNetworks) {
+ final NetworkCapabilities underlyingCaps = cm.getNetworkCapabilities(underlying);
+ for (int underlyingType : underlyingCaps.getTransportTypes()) {
+ transportTypes = ArrayUtils.appendInt(transportTypes, underlyingType);
+ }
+
+ // When we have multiple networks, we have to assume the
+ // worst-case link speed and restrictions.
+ downKbps = NetworkCapabilities.minBandwidth(downKbps,
+ underlyingCaps.getLinkDownstreamBandwidthKbps());
+ upKbps = NetworkCapabilities.minBandwidth(upKbps,
+ underlyingCaps.getLinkUpstreamBandwidthKbps());
+ metered |= !underlyingCaps.hasCapability(NET_CAPABILITY_NOT_METERED);
+ roaming |= !underlyingCaps.hasCapability(NET_CAPABILITY_NOT_ROAMING);
+ }
+ }
+
+ caps.setTransportTypes(transportTypes);
+ caps.setLinkDownstreamBandwidthKbps(downKbps);
+ caps.setLinkUpstreamBandwidthKbps(upKbps);
+ if (metered) {
+ caps.removeCapability(NET_CAPABILITY_NOT_METERED);
+ } else {
+ caps.addCapability(NET_CAPABILITY_NOT_METERED);
+ }
+ if (roaming) {
+ caps.removeCapability(NET_CAPABILITY_NOT_ROAMING);
+ } else {
+ caps.addCapability(NET_CAPABILITY_NOT_ROAMING);
+ }
+ }
+
/**
* Chooses whether to force all connections to go though VPN.
*
@@ -1344,6 +1404,7 @@
}
}
}
+ updateCapabilities();
return true;
}
diff --git a/services/core/java/com/android/server/net/OWNERS b/services/core/java/com/android/server/net/OWNERS
index 061fd8d..6b77d83 100644
--- a/services/core/java/com/android/server/net/OWNERS
+++ b/services/core/java/com/android/server/net/OWNERS
@@ -2,7 +2,7 @@
ek@google.com
hugobenichi@google.com
-jsharkey@google.com
+jsharkey@android.com
lorenzo@google.com
satk@google.com
silberst@google.com
diff --git a/services/core/java/com/android/server/pm/BackgroundDexOptService.java b/services/core/java/com/android/server/pm/BackgroundDexOptService.java
index 6749afb..e5c48cc 100644
--- a/services/core/java/com/android/server/pm/BackgroundDexOptService.java
+++ b/services/core/java/com/android/server/pm/BackgroundDexOptService.java
@@ -18,7 +18,6 @@
import static com.android.server.pm.PackageManagerService.DEBUG_DEXOPT;
-import android.app.AlarmManager;
import android.app.job.JobInfo;
import android.app.job.JobParameters;
import android.app.job.JobScheduler;
@@ -38,8 +37,10 @@
import com.android.server.pm.dex.DexManager;
import com.android.server.LocalServices;
import com.android.server.PinnerService;
+import com.android.server.pm.dex.DexoptOptions;
import java.io.File;
+import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.TimeUnit;
@@ -73,6 +74,9 @@
// Optimizations should be aborted. No space left on device.
private static final int OPTIMIZE_ABORT_NO_SPACE_LEFT = 3;
+ // Used for calculating space threshold for downgrading unused apps.
+ private static final int LOW_THRESHOLD_MULTIPLIER_FOR_DOWNGRADE = 2;
+
/**
* Set of failed packages remembered across job runs.
*/
@@ -92,6 +96,9 @@
private final File mDataDir = Environment.getDataDirectory();
+ private static final long mDowngradeUnusedAppsThresholdInMillis =
+ getDowngradeUnusedAppsThresholdInMillis();
+
public static void schedule(Context context) {
JobScheduler js = (JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE);
@@ -211,11 +218,10 @@
// Unfortunately this will also means that "pm.dexopt.boot=speed-profile" will
// behave differently than "pm.dexopt.bg-dexopt=speed-profile" but that's a
// trade-off worth doing to save boot time work.
- int result = pm.performDexOptWithStatus(pkg,
- /* checkProfiles */ false,
+ int result = pm.performDexOptWithStatus(new DexoptOptions(
+ pkg,
PackageManagerService.REASON_BOOT,
- /* force */ false,
- /* bootComplete */ true);
+ DexoptOptions.DEXOPT_BOOT_COMPLETE));
if (result == PackageDexOptimizer.DEX_OPT_PERFORMED) {
updatedPackages.add(pkg);
}
@@ -243,7 +249,8 @@
}
// Optimize the given packages and return the optimization result (one of the OPTIMIZE_* codes).
- private int idleOptimization(PackageManagerService pm, ArraySet<String> pkgs, Context context) {
+ private int idleOptimization(PackageManagerService pm, ArraySet<String> pkgs,
+ Context context) {
Log.i(TAG, "Performing idle optimizations");
// If post-boot update is still running, request that it exits early.
mExitPostBootUpdate.set(true);
@@ -274,9 +281,16 @@
long lowStorageThreshold, boolean is_for_primary_dex,
ArraySet<String> failedPackageNames) {
ArraySet<String> updatedPackages = new ArraySet<>();
+ Set<String> unusedPackages = pm.getUnusedPackages(mDowngradeUnusedAppsThresholdInMillis);
+ // Only downgrade apps when space is low on device.
+ // Threshold is selected above the lowStorageThreshold so that we can pro-actively clean
+ // up disk before user hits the actual lowStorageThreshold.
+ final long lowStorageThresholdForDowngrade = LOW_THRESHOLD_MULTIPLIER_FOR_DOWNGRADE *
+ lowStorageThreshold;
+ boolean shouldDowngrade = shouldDowngrade(lowStorageThresholdForDowngrade);
for (String pkg : pkgs) {
int abort_code = abortIdleOptimizations(lowStorageThreshold);
- if (abort_code != OPTIMIZE_CONTINUE) {
+ if (abort_code == OPTIMIZE_ABORT_BY_JOB_SCHEDULER) {
return abort_code;
}
@@ -284,30 +298,58 @@
if (failedPackageNames.contains(pkg)) {
// Skip previously failing package
continue;
- } else {
- // Conservatively add package to the list of failing ones in case performDexOpt
- // never returns.
- failedPackageNames.add(pkg);
}
}
+ int reason;
+ boolean downgrade;
+ // Downgrade unused packages.
+ if (unusedPackages.contains(pkg) && shouldDowngrade) {
+ // This applies for system apps or if packages location is not a directory, i.e.
+ // monolithic install.
+ if (is_for_primary_dex && !pm.canHaveOatDir(pkg)) {
+ // For apps that don't have the oat directory, instead of downgrading,
+ // remove their compiler artifacts from dalvik cache.
+ pm.deleteOatArtifactsOfPackage(pkg);
+ continue;
+ } else {
+ reason = PackageManagerService.REASON_INACTIVE_PACKAGE_DOWNGRADE;
+ downgrade = true;
+ }
+ } else if (abort_code != OPTIMIZE_ABORT_NO_SPACE_LEFT) {
+ reason = PackageManagerService.REASON_BACKGROUND_DEXOPT;
+ downgrade = false;
+ } else {
+ // can't dexopt because of low space.
+ continue;
+ }
+
+ synchronized (failedPackageNames) {
+ // Conservatively add package to the list of failing ones in case
+ // performDexOpt never returns.
+ failedPackageNames.add(pkg);
+ }
+
// Optimize package if needed. Note that there can be no race between
// concurrent jobs because PackageDexOptimizer.performDexOpt is synchronized.
boolean success;
+ int dexoptFlags =
+ DexoptOptions.DEXOPT_CHECK_FOR_PROFILES_UPDATES |
+ DexoptOptions.DEXOPT_BOOT_COMPLETE |
+ (downgrade ? DexoptOptions.DEXOPT_DOWNGRADE : 0) |
+ DexoptOptions.DEXOPT_IDLE_BACKGROUND_JOB;
if (is_for_primary_dex) {
- int result = pm.performDexOptWithStatus(pkg,
- /* checkProfiles */ true,
+ int result = pm.performDexOptWithStatus(new DexoptOptions(pkg,
PackageManagerService.REASON_BACKGROUND_DEXOPT,
- /* force */ false,
- /* bootComplete */ true);
+ dexoptFlags));
success = result != PackageDexOptimizer.DEX_OPT_FAILED;
if (result == PackageDexOptimizer.DEX_OPT_PERFORMED) {
updatedPackages.add(pkg);
}
} else {
- success = pm.performDexOptSecondary(pkg,
+ success = pm.performDexOpt(new DexoptOptions(pkg,
PackageManagerService.REASON_BACKGROUND_DEXOPT,
- /* force */ false);
+ dexoptFlags | DexoptOptions.DEXOPT_ONLY_SECONDARY_DEX));
}
if (success) {
// Dexopt succeeded, remove package from the list of failing ones.
@@ -347,6 +389,16 @@
return OPTIMIZE_CONTINUE;
}
+ // Evaluate whether apps should be downgraded.
+ private boolean shouldDowngrade(long lowStorageThresholdForDowngrade) {
+ long usableSpace = mDataDir.getUsableSpace();
+ if (usableSpace < lowStorageThresholdForDowngrade) {
+ return true;
+ }
+
+ return false;
+ }
+
/**
* Execute the idle optimizations immediately.
*/
@@ -415,4 +467,14 @@
pinnerService.update(updatedPackages);
}
}
+
+ private static long getDowngradeUnusedAppsThresholdInMillis() {
+ final String sysPropKey = "pm.dexopt.downgrade_after_inactive_days";
+ String sysPropValue = SystemProperties.get(sysPropKey);
+ if (sysPropValue == null || sysPropValue.isEmpty()) {
+ Log.w(TAG, "SysProp " + sysPropKey + " not set");
+ return Long.MAX_VALUE;
+ }
+ return TimeUnit.DAYS.toMillis(Long.parseLong(sysPropValue));
+ }
}
diff --git a/services/core/java/com/android/server/pm/Installer.java b/services/core/java/com/android/server/pm/Installer.java
index 5c4c040..210eb13 100644
--- a/services/core/java/com/android/server/pm/Installer.java
+++ b/services/core/java/com/android/server/pm/Installer.java
@@ -56,6 +56,8 @@
public static final int DEXOPT_STORAGE_CE = 1 << 7;
/** Indicates that the dex file passed to dexopt in on DE storage. */
public static final int DEXOPT_STORAGE_DE = 1 << 8;
+ /** Indicates that dexopt is invoked from the background service. */
+ public static final int DEXOPT_IDLE_BACKGROUND_JOB = 1 << 9;
// NOTE: keep in sync with installd
public static final int FLAG_CLEAR_CACHE_ONLY = 1 << 8;
@@ -258,7 +260,7 @@
public long[] getExternalSize(String uuid, int userId, int flags, int[] appIds)
throws InstallerException {
- if (!checkBeforeRemote()) return new long[4];
+ if (!checkBeforeRemote()) return new long[6];
try {
return mInstalld.getExternalSize(uuid, userId, flags, appIds);
} catch (Exception e) {
@@ -279,13 +281,13 @@
public void dexopt(String apkPath, int uid, @Nullable String pkgName, String instructionSet,
int dexoptNeeded, @Nullable String outputPath, int dexFlags,
String compilerFilter, @Nullable String volumeUuid, @Nullable String sharedLibraries,
- @Nullable String seInfo)
+ @Nullable String seInfo, boolean downgrade)
throws InstallerException {
assertValidInstructionSet(instructionSet);
if (!checkBeforeRemote()) return;
try {
mInstalld.dexopt(apkPath, uid, pkgName, instructionSet, dexoptNeeded, outputPath,
- dexFlags, compilerFilter, volumeUuid, sharedLibraries, seInfo);
+ dexFlags, compilerFilter, volumeUuid, sharedLibraries, seInfo, downgrade);
} catch (Exception e) {
throw InstallerException.from(e);
}
diff --git a/services/core/java/com/android/server/pm/OtaDexoptService.java b/services/core/java/com/android/server/pm/OtaDexoptService.java
index a43f8af..73ac057 100644
--- a/services/core/java/com/android/server/pm/OtaDexoptService.java
+++ b/services/core/java/com/android/server/pm/OtaDexoptService.java
@@ -18,7 +18,6 @@
import static com.android.server.pm.InstructionSets.getAppDexInstructionSets;
import static com.android.server.pm.InstructionSets.getDexCodeInstructionSets;
-import static com.android.server.pm.PackageManagerServiceCompilerMapping.getCompilerFilterForReason;
import android.annotation.Nullable;
import android.content.Context;
@@ -30,17 +29,16 @@
import android.os.ServiceManager;
import android.os.ShellCallback;
import android.os.storage.StorageManager;
-import android.text.TextUtils;
import android.util.Log;
import android.util.Slog;
import com.android.internal.logging.MetricsLogger;
import com.android.server.pm.Installer.InstallerException;
+import com.android.server.pm.dex.DexoptOptions;
import java.io.File;
import java.io.FileDescriptor;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.TimeUnit;
@@ -55,7 +53,8 @@
private final static boolean DEBUG_DEXOPT = true;
// The synthetic library dependencies denoting "no checks."
- private final static String[] NO_LIBRARIES = new String[] { "&" };
+ private final static String[] NO_LIBRARIES =
+ new String[] { PackageDexOptimizer.SKIP_SHARED_LIBRARY_CHECK };
// The amount of "available" (free - low threshold) space necessary at the start of an OTA to
// not bulk-delete unused apps' odex files.
@@ -261,11 +260,12 @@
public void dexopt(String apkPath, int uid, @Nullable String pkgName,
String instructionSet, int dexoptNeeded, @Nullable String outputPath,
int dexFlags, String compilerFilter, @Nullable String volumeUuid,
- @Nullable String sharedLibraries, @Nullable String seInfo) throws InstallerException {
+ @Nullable String sharedLibraries, @Nullable String seInfo, boolean downgrade)
+ throws InstallerException {
final StringBuilder builder = new StringBuilder();
- // The version. Right now it's 2.
- builder.append("2 ");
+ // The version. Right now it's 3.
+ builder.append("3 ");
builder.append("dexopt");
@@ -280,6 +280,7 @@
encodeParameter(builder, volumeUuid);
encodeParameter(builder, sharedLibraries);
encodeParameter(builder, seInfo);
+ encodeParameter(builder, downgrade);
commands.add(builder.toString());
}
@@ -314,12 +315,18 @@
libraryDependencies = NO_LIBRARIES;
}
+
optimizer.performDexOpt(pkg, libraryDependencies,
- null /* ISAs */, false /* checkProfiles */,
- getCompilerFilterForReason(compilationReason),
+ null /* ISAs */,
null /* CompilerStats.PackageStats */,
- mPackageManagerService.getDexManager().isUsedByOtherApps(pkg.packageName),
- true /* bootComplete */);
+ mPackageManagerService.getDexManager().getPackageUseInfoOrDefault(pkg.packageName),
+ new DexoptOptions(pkg.packageName, compilationReason,
+ DexoptOptions.DEXOPT_BOOT_COMPLETE));
+
+ mPackageManagerService.getDexManager().dexoptSecondaryDex(
+ new DexoptOptions(pkg.packageName, compilationReason,
+ DexoptOptions.DEXOPT_ONLY_SECONDARY_DEX |
+ DexoptOptions.DEXOPT_BOOT_COMPLETE));
return commands;
}
diff --git a/services/core/java/com/android/server/pm/PackageDexOptimizer.java b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
index 7e9596a..401eb62 100644
--- a/services/core/java/com/android/server/pm/PackageDexOptimizer.java
+++ b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
@@ -19,9 +19,7 @@
import android.annotation.Nullable;
import android.content.Context;
import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageInfo;
import android.content.pm.PackageParser;
-import android.os.Environment;
import android.os.FileUtils;
import android.os.PowerManager;
import android.os.SystemClock;
@@ -30,17 +28,21 @@
import android.os.WorkSource;
import android.util.Log;
import android.util.Slog;
-import android.util.SparseArray;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.IndentingPrintWriter;
import com.android.server.pm.Installer.InstallerException;
+import com.android.server.pm.dex.DexManager;
+import com.android.server.pm.dex.DexoptOptions;
+import com.android.server.pm.dex.DexoptUtils;
+import com.android.server.pm.dex.PackageDexUsage;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.List;
-import java.util.Set;
+import java.util.Map;
import dalvik.system.DexFile;
@@ -52,6 +54,7 @@
import static com.android.server.pm.Installer.DEXOPT_FORCE;
import static com.android.server.pm.Installer.DEXOPT_STORAGE_CE;
import static com.android.server.pm.Installer.DEXOPT_STORAGE_DE;
+import static com.android.server.pm.Installer.DEXOPT_IDLE_BACKGROUND_JOB;
import static com.android.server.pm.InstructionSets.getAppDexInstructionSets;
import static com.android.server.pm.InstructionSets.getDexCodeInstructionSets;
@@ -107,9 +110,9 @@
return false;
}
- // We do not dexopt a priv-app package when pm.dexopt.priv-apps is false.
+ // We do not dexopt a priv-app package when pm.dexopt.priv-apps-oob is true.
if (pkg.isPrivilegedApp()) {
- return SystemProperties.getBoolean("pm.dexopt.priv-apps", true);
+ return !SystemProperties.getBoolean("pm.dexopt.priv-apps-oob", false);
}
return true;
@@ -123,17 +126,16 @@
* synchronized on {@link #mInstallLock}.
*/
int performDexOpt(PackageParser.Package pkg, String[] sharedLibraries,
- String[] instructionSets, boolean checkProfiles, String targetCompilationFilter,
- CompilerStats.PackageStats packageStats, boolean isUsedByOtherApps,
- boolean bootComplete) {
+ String[] instructionSets, CompilerStats.PackageStats packageStats,
+ PackageDexUsage.PackageUseInfo packageUseInfo, DexoptOptions options) {
if (!canOptimizePackage(pkg)) {
return DEX_OPT_SKIPPED;
}
synchronized (mInstallLock) {
final long acquireTime = acquireWakeLockLI(pkg.applicationInfo.uid);
try {
- return performDexOptLI(pkg, sharedLibraries, instructionSets, checkProfiles,
- targetCompilationFilter, packageStats, isUsedByOtherApps, bootComplete);
+ return performDexOptLI(pkg, sharedLibraries, instructionSets,
+ packageStats, packageUseInfo, options);
} finally {
releaseWakeLockLI(acquireTime);
}
@@ -146,46 +148,73 @@
*/
@GuardedBy("mInstallLock")
private int performDexOptLI(PackageParser.Package pkg, String[] sharedLibraries,
- String[] targetInstructionSets, boolean checkForProfileUpdates,
- String targetCompilerFilter, CompilerStats.PackageStats packageStats,
- boolean isUsedByOtherApps, boolean bootComplete) {
+ String[] targetInstructionSets, CompilerStats.PackageStats packageStats,
+ PackageDexUsage.PackageUseInfo packageUseInfo, DexoptOptions options) {
final String[] instructionSets = targetInstructionSets != null ?
targetInstructionSets : getAppDexInstructionSets(pkg.applicationInfo);
final String[] dexCodeInstructionSets = getDexCodeInstructionSets(instructionSets);
final List<String> paths = pkg.getAllCodePaths();
final int sharedGid = UserHandle.getSharedAppGid(pkg.applicationInfo.uid);
- final String compilerFilter = getRealCompilerFilter(pkg.applicationInfo,
- targetCompilerFilter, isUsedByOtherApps);
- final boolean profileUpdated = checkForProfileUpdates &&
- isProfileUpdated(pkg, sharedGid, compilerFilter);
+ // Get the class loader context dependencies.
+ // For each code path in the package, this array contains the class loader context that
+ // needs to be passed to dexopt in order to ensure correct optimizations.
+ boolean[] pathsWithCode = new boolean[paths.size()];
+ pathsWithCode[0] = (pkg.applicationInfo.flags & ApplicationInfo.FLAG_HAS_CODE) != 0;
+ for (int i = 1; i < paths.size(); i++) {
+ pathsWithCode[i] = (pkg.splitFlags[i - 1] & ApplicationInfo.FLAG_HAS_CODE) != 0;
+ }
+ String[] classLoaderContexts = DexoptUtils.getClassLoaderContexts(
+ pkg.applicationInfo, sharedLibraries, pathsWithCode);
- final String sharedLibrariesPath = getSharedLibrariesPath(sharedLibraries);
- // Get the dexopt flags after getRealCompilerFilter to make sure we get the correct flags.
- final int dexoptFlags = getDexFlags(pkg, compilerFilter, bootComplete);
- // Get the dependencies of each split in the package. For each code path in the package,
- // this array contains the relative paths of each split it depends on, separated by colons.
- String[] splitDependencies = getSplitDependencies(pkg);
+ // Sanity check that we do not call dexopt with inconsistent data.
+ if (paths.size() != classLoaderContexts.length) {
+ String[] splitCodePaths = pkg.applicationInfo.getSplitCodePaths();
+ throw new IllegalStateException("Inconsistent information "
+ + "between PackageParser.Package and its ApplicationInfo. "
+ + "pkg.getAllCodePaths=" + paths
+ + " pkg.applicationInfo.getBaseCodePath=" + pkg.applicationInfo.getBaseCodePath()
+ + " pkg.applicationInfo.getSplitCodePaths="
+ + (splitCodePaths == null ? "null" : Arrays.toString(splitCodePaths)));
+ }
int result = DEX_OPT_SKIPPED;
for (int i = 0; i < paths.size(); i++) {
// Skip paths that have no code.
- if ((i == 0 && (pkg.applicationInfo.flags & ApplicationInfo.FLAG_HAS_CODE) == 0) ||
- (i != 0 && (pkg.splitFlags[i - 1] & ApplicationInfo.FLAG_HAS_CODE) == 0)) {
+ if (!pathsWithCode[i]) {
continue;
}
+ if (classLoaderContexts[i] == null) {
+ throw new IllegalStateException("Inconsistent information in the "
+ + "package structure. A split is marked to contain code "
+ + "but has no dependency listed. Index=" + i + " path=" + paths.get(i));
+ }
+
// Append shared libraries with split dependencies for this split.
String path = paths.get(i);
- String sharedLibrariesPathWithSplits;
- if (sharedLibrariesPath != null && splitDependencies[i] != null) {
- sharedLibrariesPathWithSplits = sharedLibrariesPath + ":" + splitDependencies[i];
- } else {
- sharedLibrariesPathWithSplits =
- splitDependencies[i] != null ? splitDependencies[i] : sharedLibrariesPath;
+ if (options.getSplitName() != null) {
+ // We are asked to compile only a specific split. Check that the current path is
+ // what we are looking for.
+ if (!options.getSplitName().equals(new File(path).getName())) {
+ continue;
+ }
}
+
+ final boolean isUsedByOtherApps = options.isDexoptAsSharedLibrary()
+ || packageUseInfo.isUsedByOtherApps(path);
+ final String compilerFilter = getRealCompilerFilter(pkg.applicationInfo,
+ options.getCompilerFilter(), isUsedByOtherApps);
+ final boolean profileUpdated = options.isCheckForProfileUpdates() &&
+ isProfileUpdated(pkg, sharedGid, compilerFilter);
+
+ // Get the dexopt flags after getRealCompilerFilter to make sure we get the correct
+ // flags.
+ final int dexoptFlags = getDexFlags(pkg, compilerFilter, options);
+
for (String dexCodeIsa : dexCodeInstructionSets) {
- int newResult = dexOptPath(pkg, path, dexCodeIsa, compilerFilter, profileUpdated,
- sharedLibrariesPathWithSplits, dexoptFlags, sharedGid, packageStats);
+ int newResult = dexOptPath(pkg, path, dexCodeIsa, compilerFilter,
+ profileUpdated, classLoaderContexts[i], dexoptFlags, sharedGid,
+ packageStats, options.isDowngrade());
// The end result is:
// - FAILED if any path failed,
// - PERFORMED if at least one path needed compilation,
@@ -208,9 +237,10 @@
*/
@GuardedBy("mInstallLock")
private int dexOptPath(PackageParser.Package pkg, String path, String isa,
- String compilerFilter, boolean profileUpdated, String sharedLibrariesPath,
- int dexoptFlags, int uid, CompilerStats.PackageStats packageStats) {
- int dexoptNeeded = getDexoptNeeded(path, isa, compilerFilter, profileUpdated);
+ String compilerFilter, boolean profileUpdated, String classLoaderContext,
+ int dexoptFlags, int uid, CompilerStats.PackageStats packageStats, boolean downgrade) {
+ int dexoptNeeded = getDexoptNeeded(path, isa, compilerFilter, classLoaderContext,
+ profileUpdated, downgrade);
if (Math.abs(dexoptNeeded) == DexFile.NO_DEXOPT_NEEDED) {
return DEX_OPT_SKIPPED;
}
@@ -223,14 +253,18 @@
Log.i(TAG, "Running dexopt (dexoptNeeded=" + dexoptNeeded + ") on: " + path
+ " pkg=" + pkg.applicationInfo.packageName + " isa=" + isa
+ " dexoptFlags=" + printDexoptFlags(dexoptFlags)
- + " target-filter=" + compilerFilter + " oatDir=" + oatDir
- + " sharedLibraries=" + sharedLibrariesPath);
+ + " targetFilter=" + compilerFilter + " oatDir=" + oatDir
+ + " classLoaderContext=" + classLoaderContext);
try {
long startTime = System.currentTimeMillis();
+ // TODO: Consider adding 2 different APIs for primary and secondary dexopt.
+ // installd only uses downgrade flag for secondary dex files and ignores it for
+ // primary dex files.
mInstaller.dexopt(path, uid, pkg.packageName, isa, dexoptNeeded, oatDir, dexoptFlags,
- compilerFilter, pkg.volumeUuid, sharedLibrariesPath, pkg.applicationInfo.seInfo);
+ compilerFilter, pkg.volumeUuid, classLoaderContext, pkg.applicationInfo.seInfo,
+ false /* downgrade*/);
if (packageStats != null) {
long endTime = System.currentTimeMillis();
@@ -257,13 +291,12 @@
* throwing exceptions). Or maybe make a separate call to installd to get DexOptNeeded, though
* that seems wasteful.
*/
- public int dexOptSecondaryDexPath(ApplicationInfo info, String path, Set<String> isas,
- String compilerFilter, boolean isUsedByOtherApps) {
+ public int dexOptSecondaryDexPath(ApplicationInfo info, String path,
+ PackageDexUsage.DexUseInfo dexUseInfo, DexoptOptions options) {
synchronized (mInstallLock) {
final long acquireTime = acquireWakeLockLI(info.uid);
try {
- return dexOptSecondaryDexPathLI(info, path, isas, compilerFilter,
- isUsedByOtherApps);
+ return dexOptSecondaryDexPathLI(info, path, dexUseInfo, options);
} finally {
releaseWakeLockLI(acquireTime);
}
@@ -304,13 +337,19 @@
}
@GuardedBy("mInstallLock")
- private int dexOptSecondaryDexPathLI(ApplicationInfo info, String path, Set<String> isas,
- String compilerFilter, boolean isUsedByOtherApps) {
- compilerFilter = getRealCompilerFilter(info, compilerFilter, isUsedByOtherApps);
+ private int dexOptSecondaryDexPathLI(ApplicationInfo info, String path,
+ PackageDexUsage.DexUseInfo dexUseInfo, DexoptOptions options) {
+ if (options.isDexoptOnlySharedDex() && !dexUseInfo.isUsedByOtherApps()) {
+ // We are asked to optimize only the dex files used by other apps and this is not
+ // on of them: skip it.
+ return DEX_OPT_SKIPPED;
+ }
+
+ String compilerFilter = getRealCompilerFilter(info, options.getCompilerFilter(),
+ dexUseInfo.isUsedByOtherApps());
// Get the dexopt flags after getRealCompilerFilter to make sure we get the correct flags.
// Secondary dex files are currently not compiled at boot.
- int dexoptFlags = getDexFlags(info, compilerFilter, /* bootComplete */ true)
- | DEXOPT_SECONDARY_DEX;
+ int dexoptFlags = getDexFlags(info, compilerFilter, options) | DEXOPT_SECONDARY_DEX;
// Check the app storage and add the appropriate flags.
if (info.deviceProtectedDataDir != null &&
FileUtils.contains(info.deviceProtectedDataDir, path)) {
@@ -323,19 +362,27 @@
return DEX_OPT_FAILED;
}
Log.d(TAG, "Running dexopt on: " + path
- + " pkg=" + info.packageName + " isa=" + isas
+ + " pkg=" + info.packageName + " isa=" + dexUseInfo.getLoaderIsas()
+ " dexoptFlags=" + printDexoptFlags(dexoptFlags)
+ " target-filter=" + compilerFilter);
+ // TODO(calin): b/64530081 b/66984396. Use SKIP_SHARED_LIBRARY_CHECK for the context
+ // (instead of dexUseInfo.getClassLoaderContext()) in order to compile secondary dex files
+ // in isolation (and avoid to extract/verify the main apk if it's in the class path).
+ // Note this trades correctness for performance since the resulting slow down is
+ // unacceptable in some cases until b/64530081 is fixed.
+ String classLoaderContext = SKIP_SHARED_LIBRARY_CHECK;
+
try {
- for (String isa : isas) {
+ for (String isa : dexUseInfo.getLoaderIsas()) {
// Reuse the same dexopt path as for the primary apks. We don't need all the
// arguments as some (dexopNeeded and oatDir) will be computed by installd because
// system server cannot read untrusted app content.
// TODO(calin): maybe add a separate call.
mInstaller.dexopt(path, info.uid, info.packageName, isa, /*dexoptNeeded*/ 0,
/*oatDir*/ null, dexoptFlags,
- compilerFilter, info.volumeUuid, SKIP_SHARED_LIBRARY_CHECK, info.seInfoUser);
+ compilerFilter, info.volumeUuid, classLoaderContext, info.seInfoUser,
+ options.isDowngrade());
}
return DEX_OPT_PERFORMED;
@@ -363,26 +410,51 @@
/**
* Dumps the dexopt state of the given package {@code pkg} to the given {@code PrintWriter}.
*/
- void dumpDexoptState(IndentingPrintWriter pw, PackageParser.Package pkg) {
+ void dumpDexoptState(IndentingPrintWriter pw, PackageParser.Package pkg,
+ PackageDexUsage.PackageUseInfo useInfo) {
final String[] instructionSets = getAppDexInstructionSets(pkg.applicationInfo);
final String[] dexCodeInstructionSets = getDexCodeInstructionSets(instructionSets);
final List<String> paths = pkg.getAllCodePathsExcludingResourceOnly();
- for (String instructionSet : dexCodeInstructionSets) {
- pw.println("Instruction Set: " + instructionSet);
- pw.increaseIndent();
- for (String path : paths) {
- String status = null;
- try {
- status = DexFile.getDexFileStatus(path, instructionSet);
- } catch (IOException ioe) {
- status = "[Exception]: " + ioe.getMessage();
- }
- pw.println("path: " + path);
- pw.println("status: " + status);
- }
- pw.decreaseIndent();
+ for (String path : paths) {
+ pw.println("path: " + path);
+ pw.increaseIndent();
+
+ for (String isa : dexCodeInstructionSets) {
+ String status = null;
+ try {
+ status = DexFile.getDexFileStatus(path, isa);
+ } catch (IOException ioe) {
+ status = "[Exception]: " + ioe.getMessage();
+ }
+ pw.println(isa + ": " + status);
+ }
+
+ if (useInfo.isUsedByOtherApps(path)) {
+ pw.println("used by other apps: " + useInfo.getLoadingPackages(path));
+ }
+
+ Map<String, PackageDexUsage.DexUseInfo> dexUseInfoMap = useInfo.getDexUseInfoMap();
+
+ if (!dexUseInfoMap.isEmpty()) {
+ pw.println("known secondary dex files:");
+ pw.increaseIndent();
+ for (Map.Entry<String, PackageDexUsage.DexUseInfo> e : dexUseInfoMap.entrySet()) {
+ String dex = e.getKey();
+ PackageDexUsage.DexUseInfo dexUseInfo = e.getValue();
+ pw.println(dex);
+ pw.increaseIndent();
+ // TODO(calin): get the status of the oat file (needs installd call)
+ pw.println("class loader context: " + dexUseInfo.getClassLoaderContext());
+ if (dexUseInfo.isUsedByOtherApps()) {
+ pw.println("used by other apps: " + dexUseInfo.getLoadingPackages());
+ }
+ pw.decreaseIndent();
+ }
+ pw.decreaseIndent();
+ }
+ pw.decreaseIndent();
}
}
@@ -400,8 +472,9 @@
}
if (isProfileGuidedCompilerFilter(targetCompilerFilter) && isUsedByOtherApps) {
- // If the dex files is used by other apps, we cannot use profile-guided compilation.
- return getNonProfileGuidedCompilerFilter(targetCompilerFilter);
+ // If the dex files is used by other apps, apply the shared filter.
+ return PackageManagerServiceCompilerMapping.getCompilerFilterForReason(
+ PackageManagerService.REASON_SHARED);
}
return targetCompilerFilter;
@@ -412,11 +485,11 @@
* filter.
*/
private int getDexFlags(PackageParser.Package pkg, String compilerFilter,
- boolean bootComplete) {
- return getDexFlags(pkg.applicationInfo, compilerFilter, bootComplete);
+ DexoptOptions options) {
+ return getDexFlags(pkg.applicationInfo, compilerFilter, options);
}
- private int getDexFlags(ApplicationInfo info, String compilerFilter, boolean bootComplete) {
+ private int getDexFlags(ApplicationInfo info, String compilerFilter, DexoptOptions options) {
int flags = info.flags;
boolean debuggable = (flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0;
// Profile guide compiled oat files should not be public.
@@ -427,7 +500,8 @@
(isPublic ? DEXOPT_PUBLIC : 0)
| (debuggable ? DEXOPT_DEBUGGABLE : 0)
| profileFlag
- | (bootComplete ? DEXOPT_BOOTCOMPLETE : 0);
+ | (options.isBootComplete() ? DEXOPT_BOOTCOMPLETE : 0)
+ | (options.isDexoptIdleBackgroundJob() ? DEXOPT_IDLE_BACKGROUND_JOB : 0);
return adjustDexoptFlags(dexFlags);
}
@@ -436,11 +510,11 @@
* configuration (isa, compiler filter, profile).
*/
private int getDexoptNeeded(String path, String isa, String compilerFilter,
- boolean newProfile) {
+ String classLoaderContext, boolean newProfile, boolean downgrade) {
int dexoptNeeded;
try {
- dexoptNeeded = DexFile.getDexOptNeeded(path, isa, compilerFilter, newProfile,
- false /* downgrade */);
+ dexoptNeeded = DexFile.getDexOptNeeded(path, isa, compilerFilter, classLoaderContext,
+ newProfile, downgrade);
} catch (IOException ioe) {
Slog.w(TAG, "IOException reading apk: " + path, ioe);
return DEX_OPT_FAILED;
@@ -449,86 +523,6 @@
}
/**
- * Computes the shared libraries path that should be passed to dexopt.
- */
- private String getSharedLibrariesPath(String[] sharedLibraries) {
- if (sharedLibraries == null || sharedLibraries.length == 0) {
- return null;
- }
- StringBuilder sb = new StringBuilder();
- for (String lib : sharedLibraries) {
- if (sb.length() != 0) {
- sb.append(":");
- }
- sb.append(lib);
- }
- return sb.toString();
- }
-
- /**
- * Walks dependency tree and gathers the dependencies for each split in a split apk.
- * The split paths are stored as relative paths, separated by colons.
- */
- private String[] getSplitDependencies(PackageParser.Package pkg) {
- // Convert all the code paths to relative paths.
- String baseCodePath = new File(pkg.baseCodePath).getParent();
- List<String> paths = pkg.getAllCodePaths();
- String[] splitDependencies = new String[paths.size()];
- for (int i = 0; i < paths.size(); i++) {
- File pathFile = new File(paths.get(i));
- String fileName = pathFile.getName();
- paths.set(i, fileName);
-
- // Sanity check that the base paths of the splits are all the same.
- String basePath = pathFile.getParent();
- if (!basePath.equals(baseCodePath)) {
- Slog.wtf(TAG, "Split paths have different base paths: " + basePath + " and " +
- baseCodePath);
- }
- }
-
- // If there are no other dependencies, fill in the implicit dependency on the base apk.
- SparseArray<int[]> dependencies = pkg.applicationInfo.splitDependencies;
- if (dependencies == null) {
- for (int i = 1; i < paths.size(); i++) {
- splitDependencies[i] = paths.get(0);
- }
- return splitDependencies;
- }
-
- // Fill in the dependencies, skipping the base apk which has no dependencies.
- for (int i = 1; i < dependencies.size(); i++) {
- getParentDependencies(dependencies.keyAt(i), paths, dependencies, splitDependencies);
- }
-
- return splitDependencies;
- }
-
- /**
- * Recursive method to generate dependencies for a particular split.
- * The index is a key from the package's splitDependencies.
- */
- private String getParentDependencies(int index, List<String> paths,
- SparseArray<int[]> dependencies, String[] splitDependencies) {
- // The base apk is always first, and has no dependencies.
- if (index == 0) {
- return null;
- }
- // Return the result if we've computed the dependencies for this index already.
- if (splitDependencies[index] != null) {
- return splitDependencies[index];
- }
- // Get the dependencies for the parent of this index and append its path to it.
- int parent = dependencies.get(index)[0];
- String parentDependencies =
- getParentDependencies(parent, paths, dependencies, splitDependencies);
- String path = parentDependencies == null ? paths.get(parent) :
- parentDependencies + ":" + paths.get(parent);
- splitDependencies[index] = path;
- return path;
- }
-
- /**
* Checks if there is an update on the profile information of the {@code pkg}.
* If the compiler filter is not profile guided the method returns false.
*
@@ -619,6 +613,9 @@
if ((flags & DEXOPT_STORAGE_DE) == DEXOPT_STORAGE_DE) {
flagsList.add("storage_de");
}
+ if ((flags & DEXOPT_IDLE_BACKGROUND_JOB) == DEXOPT_IDLE_BACKGROUND_JOB) {
+ flagsList.add("idle_background_job");
+ }
return String.join(",", flagsList);
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index da1df78..8853aa0 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -280,6 +280,8 @@
import com.android.server.pm.Settings.DatabaseVersion;
import com.android.server.pm.Settings.VersionInfo;
import com.android.server.pm.dex.DexManager;
+import com.android.server.pm.dex.DexoptOptions;
+import com.android.server.pm.dex.PackageDexUsage;
import com.android.server.storage.DeviceStorageMonitorInternal;
import dalvik.system.CloseGuard;
@@ -559,8 +561,10 @@
public static final int REASON_INSTALL = 2;
public static final int REASON_BACKGROUND_DEXOPT = 3;
public static final int REASON_AB_OTA = 4;
+ public static final int REASON_INACTIVE_PACKAGE_DOWNGRADE = 5;
+ public static final int REASON_SHARED = 6;
- public static final int REASON_LAST = REASON_AB_OTA;
+ public static final int REASON_LAST = REASON_SHARED;
/** All dangerous permission names in the same order as the events in MetricsEvent */
private static final List<String> ALL_DANGEROUS_PERMISSIONS = Arrays.asList(
@@ -9392,24 +9396,42 @@
// Unfortunately this will also means that "pm.dexopt.boot=speed-profile" will
// behave differently than "pm.dexopt.bg-dexopt=speed-profile" but that's a
// trade-off worth doing to save boot time work.
- int dexOptStatus = performDexOptTraced(pkg.packageName,
- false /* checkProfiles */,
+ int dexoptFlags = bootComplete ? DexoptOptions.DEXOPT_BOOT_COMPLETE : 0;
+ int primaryDexOptStaus = performDexOptTraced(new DexoptOptions(
+ pkg.packageName,
compilerFilter,
- false /* force */,
- bootComplete);
- switch (dexOptStatus) {
- case PackageDexOptimizer.DEX_OPT_PERFORMED:
- numberOfPackagesOptimized++;
- break;
- case PackageDexOptimizer.DEX_OPT_SKIPPED:
- numberOfPackagesSkipped++;
- break;
- case PackageDexOptimizer.DEX_OPT_FAILED:
- numberOfPackagesFailed++;
- break;
- default:
- Log.e(TAG, "Unexpected dexopt return code " + dexOptStatus);
- break;
+ dexoptFlags));
+
+ boolean secondaryDexOptStatus = true;
+ if (pkg.isSystemApp()) {
+ // Only dexopt shared secondary dex files belonging to system apps to not slow down
+ // too much boot after an OTA.
+ int secondaryDexoptFlags = dexoptFlags |
+ DexoptOptions.DEXOPT_ONLY_SECONDARY_DEX |
+ DexoptOptions.DEXOPT_ONLY_SHARED_DEX;
+ mDexManager.dexoptSecondaryDex(new DexoptOptions(
+ pkg.packageName,
+ compilerFilter,
+ secondaryDexoptFlags));
+ }
+
+ if (secondaryDexOptStatus) {
+ switch (primaryDexOptStaus) {
+ case PackageDexOptimizer.DEX_OPT_PERFORMED:
+ numberOfPackagesOptimized++;
+ break;
+ case PackageDexOptimizer.DEX_OPT_SKIPPED:
+ numberOfPackagesSkipped++;
+ break;
+ case PackageDexOptimizer.DEX_OPT_FAILED:
+ numberOfPackagesFailed++;
+ break;
+ default:
+ Log.e(TAG, "Unexpected dexopt return code " + primaryDexOptStaus);
+ break;
+ }
+ } else {
+ numberOfPackagesFailed++;
}
}
@@ -9440,7 +9462,8 @@
}
@Override
- public void notifyDexLoad(String loadingPackageName, List<String> dexPaths, String loaderIsa) {
+ public void notifyDexLoad(String loadingPackageName, List<String> classLoaderNames,
+ List<String> classPaths, String loaderIsa) {
int userId = UserHandle.getCallingUserId();
ApplicationInfo ai = getApplicationInfo(loadingPackageName, /*flags*/ 0, userId);
if (ai == null) {
@@ -9448,7 +9471,7 @@
+ loadingPackageName + ", user=" + userId);
return;
}
- mDexManager.notifyDexLoad(ai, dexPaths, loaderIsa, userId);
+ mDexManager.notifyDexLoad(ai, classLoaderNames, classPaths, loaderIsa, userId);
}
@Override
@@ -9476,17 +9499,53 @@
}
}
+ /**
+ * Ask the package manager to perform a dex-opt with the given compiler filter.
+ *
+ * Note: exposed only for the shell command to allow moving packages explicitly to a
+ * definite state.
+ */
@Override
- public boolean performDexOpt(String packageName,
- boolean checkProfiles, int compileReason, boolean force, boolean bootComplete) {
+ public boolean performDexOptMode(String packageName,
+ boolean checkProfiles, String targetCompilerFilter, boolean force,
+ boolean bootComplete, String splitName) {
+ int flags = (checkProfiles ? DexoptOptions.DEXOPT_CHECK_FOR_PROFILES_UPDATES : 0) |
+ (force ? DexoptOptions.DEXOPT_FORCE : 0) |
+ (bootComplete ? DexoptOptions.DEXOPT_BOOT_COMPLETE : 0);
+ return performDexOpt(new DexoptOptions(packageName, targetCompilerFilter,
+ splitName, flags));
+ }
+
+ /**
+ * Ask the package manager to perform a dex-opt with the given compiler filter on the
+ * secondary dex files belonging to the given package.
+ *
+ * Note: exposed only for the shell command to allow moving packages explicitly to a
+ * definite state.
+ */
+ @Override
+ public boolean performDexOptSecondary(String packageName, String compilerFilter,
+ boolean force) {
+ int flags = DexoptOptions.DEXOPT_ONLY_SECONDARY_DEX |
+ DexoptOptions.DEXOPT_CHECK_FOR_PROFILES_UPDATES |
+ DexoptOptions.DEXOPT_BOOT_COMPLETE |
+ (force ? DexoptOptions.DEXOPT_FORCE : 0);
+ return performDexOpt(new DexoptOptions(packageName, compilerFilter, flags));
+ }
+
+ /*package*/ boolean performDexOpt(DexoptOptions options) {
if (getInstantAppPackageName(Binder.getCallingUid()) != null) {
return false;
- } else if (isInstantApp(packageName, UserHandle.getCallingUserId())) {
+ } else if (isInstantApp(options.getPackageName(), UserHandle.getCallingUserId())) {
return false;
}
- int dexoptStatus = performDexOptWithStatus(
- packageName, checkProfiles, compileReason, force, bootComplete);
- return dexoptStatus != PackageDexOptimizer.DEX_OPT_FAILED;
+
+ if (options.isDexoptOnlySecondaryDex()) {
+ return mDexManager.dexoptSecondaryDex(options);
+ } else {
+ int dexoptStatus = performDexOptWithStatus(options);
+ return dexoptStatus != PackageDexOptimizer.DEX_OPT_FAILED;
+ }
}
/**
@@ -9495,33 +9554,14 @@
* {@link PackageDexOptimizer#DEX_OPT_PERFORMED}
* {@link PackageDexOptimizer#DEX_OPT_FAILED}
*/
- /* package */ int performDexOptWithStatus(String packageName,
- boolean checkProfiles, int compileReason, boolean force, boolean bootComplete) {
- return performDexOptTraced(packageName, checkProfiles,
- getCompilerFilterForReason(compileReason), force, bootComplete);
+ /* package */ int performDexOptWithStatus(DexoptOptions options) {
+ return performDexOptTraced(options);
}
- @Override
- public boolean performDexOptMode(String packageName,
- boolean checkProfiles, String targetCompilerFilter, boolean force,
- boolean bootComplete) {
- if (getInstantAppPackageName(Binder.getCallingUid()) != null) {
- return false;
- } else if (isInstantApp(packageName, UserHandle.getCallingUserId())) {
- return false;
- }
- int dexOptStatus = performDexOptTraced(packageName, checkProfiles,
- targetCompilerFilter, force, bootComplete);
- return dexOptStatus != PackageDexOptimizer.DEX_OPT_FAILED;
- }
-
- private int performDexOptTraced(String packageName,
- boolean checkProfiles, String targetCompilerFilter, boolean force,
- boolean bootComplete) {
+ private int performDexOptTraced(DexoptOptions options) {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "dexopt");
try {
- return performDexOptInternal(packageName, checkProfiles,
- targetCompilerFilter, force, bootComplete);
+ return performDexOptInternal(options);
} finally {
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
@@ -9529,12 +9569,10 @@
// Run dexopt on a given package. Returns true if dexopt did not fail, i.e.
// if the package can now be considered up to date for the given filter.
- private int performDexOptInternal(String packageName,
- boolean checkProfiles, String targetCompilerFilter, boolean force,
- boolean bootComplete) {
+ private int performDexOptInternal(DexoptOptions options) {
PackageParser.Package p;
synchronized (mPackages) {
- p = mPackages.get(packageName);
+ p = mPackages.get(options.getPackageName());
if (p == null) {
// Package could not be found. Report failure.
return PackageDexOptimizer.DEX_OPT_FAILED;
@@ -9545,8 +9583,7 @@
long callingId = Binder.clearCallingIdentity();
try {
synchronized (mInstallLock) {
- return performDexOptInternalWithDependenciesLI(p, checkProfiles,
- targetCompilerFilter, force, bootComplete);
+ return performDexOptInternalWithDependenciesLI(p, options);
}
} finally {
Binder.restoreCallingIdentity(callingId);
@@ -9566,12 +9603,11 @@
}
private int performDexOptInternalWithDependenciesLI(PackageParser.Package p,
- boolean checkProfiles, String targetCompilerFilter,
- boolean force, boolean bootComplete) {
+ DexoptOptions options) {
// Select the dex optimizer based on the force parameter.
// Note: The force option is rarely used (cmdline input for testing, mostly), so it's OK to
// allocate an object here.
- PackageDexOptimizer pdo = force
+ PackageDexOptimizer pdo = options.isForce()
? new PackageDexOptimizer.ForcedUpdatePackageDexOptimizer(mPackageDexOptimizer)
: mPackageDexOptimizer;
@@ -9585,39 +9621,19 @@
Collection<PackageParser.Package> deps = findSharedNonSystemLibraries(p);
final String[] instructionSets = getAppDexInstructionSets(p.applicationInfo);
if (!deps.isEmpty()) {
+ DexoptOptions libraryOptions = new DexoptOptions(options.getPackageName(),
+ options.getCompilerFilter(), options.getSplitName(),
+ options.getFlags() | DexoptOptions.DEXOPT_AS_SHARED_LIBRARY);
for (PackageParser.Package depPackage : deps) {
// TODO: Analyze and investigate if we (should) profile libraries.
pdo.performDexOpt(depPackage, null /* sharedLibraries */, instructionSets,
- false /* checkProfiles */,
- targetCompilerFilter,
getOrCreateCompilerPackageStats(depPackage),
- true /* isUsedByOtherApps */,
- bootComplete);
+ mDexManager.getPackageUseInfoOrDefault(depPackage.packageName), libraryOptions);
}
}
- return pdo.performDexOpt(p, p.usesLibraryFiles, instructionSets, checkProfiles,
- targetCompilerFilter, getOrCreateCompilerPackageStats(p),
- mDexManager.isUsedByOtherApps(p.packageName), bootComplete);
- }
-
- // Performs dexopt on the used secondary dex files belonging to the given package.
- // Returns true if all dex files were process successfully (which could mean either dexopt or
- // skip). Returns false if any of the files caused errors.
- @Override
- public boolean performDexOptSecondary(String packageName, String compilerFilter,
- boolean force) {
- if (getInstantAppPackageName(Binder.getCallingUid()) != null) {
- return false;
- } else if (isInstantApp(packageName, UserHandle.getCallingUserId())) {
- return false;
- }
- mDexManager.reconcileSecondaryDexFiles(packageName);
- return mDexManager.dexoptSecondaryDex(packageName, compilerFilter, force);
- }
-
- public boolean performDexOptSecondary(String packageName, int compileReason,
- boolean force) {
- return mDexManager.dexoptSecondaryDex(packageName, compileReason, force);
+ return pdo.performDexOpt(p, p.usesLibraryFiles, instructionSets,
+ getOrCreateCompilerPackageStats(p),
+ mDexManager.getPackageUseInfoOrDefault(p.packageName), options);
}
/**
@@ -9743,6 +9759,7 @@
public void shutdown() {
mPackageUsage.writeNow(mPackages);
mCompilerStats.writeNow();
+ mDexManager.writePackageDexUsageNow();
}
@Override
@@ -9793,10 +9810,11 @@
// Whoever is calling forceDexOpt wants a compiled package.
// Don't use profiles since that may cause compilation to be skipped.
- final int res = performDexOptInternalWithDependenciesLI(pkg,
- false /* checkProfiles */, getDefaultCompilerFilter(),
- true /* force */,
- true /* bootComplete */);
+ final int res = performDexOptInternalWithDependenciesLI(
+ pkg,
+ new DexoptOptions(packageName,
+ getDefaultCompilerFilter(),
+ DexoptOptions.DEXOPT_FORCE | DexoptOptions.DEXOPT_BOOT_COMPLETE));
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
if (res != PackageDexOptimizer.DEX_OPT_PERFORMED) {
@@ -10252,7 +10270,7 @@
if (Build.IS_DEBUGGABLE &&
pkg.isPrivilegedApp() &&
- !SystemProperties.getBoolean("pm.dexopt.priv-apps", true)) {
+ SystemProperties.getBoolean("pm.dexopt.priv-apps-oob", false)) {
PackageManagerServiceUtils.logPackageHasUncompressedCode(pkg);
}
@@ -16227,7 +16245,7 @@
}
}
- private void removeDexFiles(List<String> allCodePaths, String[] instructionSets) {
+ void removeDexFiles(List<String> allCodePaths, String[] instructionSets) {
if (!allCodePaths.isEmpty()) {
if (instructionSets == null) {
throw new IllegalStateException("instructionSet == null");
@@ -18246,34 +18264,6 @@
Slog.e(TAG, "updateAllSharedLibrariesLPw failed: " + e.getMessage());
}
}
-
- // dexopt can take some time to complete, so, for instant apps, we skip this
- // step during installation. Instead, we'll take extra time the first time the
- // instant app starts. It's preferred to do it this way to provide continuous
- // progress to the user instead of mysteriously blocking somewhere in the
- // middle of running an instant app. The default behaviour can be overridden
- // via gservices.
- if (!instantApp || Global.getInt(
- mContext.getContentResolver(), Global.INSTANT_APP_DEXOPT_ENABLED, 0) != 0) {
- Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "dexopt");
- // Do not run PackageDexOptimizer through the local performDexOpt
- // method because `pkg` may not be in `mPackages` yet.
- //
- // Also, don't fail application installs if the dexopt step fails.
- mPackageDexOptimizer.performDexOpt(pkg, pkg.usesLibraryFiles,
- null /* instructionSets */, false /* checkProfiles */,
- getCompilerFilterForReason(REASON_INSTALL),
- getOrCreateCompilerPackageStats(pkg),
- mDexManager.isUsedByOtherApps(pkg.packageName),
- true /* bootComplete */);
- Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
- }
-
- // Notify BackgroundDexOptService that the package has been changed.
- // If this is an update of a package which used to fail to compile,
- // BDOS will remove it from its blacklist.
- // TODO: Layering violation
- BackgroundDexOptService.notifyPackageChanged(pkg.packageName);
}
if (!args.doRename(res.returnCode, pkg, oldCodePath)) {
@@ -18281,6 +18271,50 @@
return;
}
+ // Verify if we need to dexopt the app.
+ //
+ // NOTE: it is *important* to call dexopt after doRename which will sync the
+ // package data from PackageParser.Package and its corresponding ApplicationInfo.
+ //
+ // We only need to dexopt if the package meets ALL of the following conditions:
+ // 1) it is not forward locked.
+ // 2) it is not on on an external ASEC container.
+ // 3) it is not an instant app or if it is then dexopt is enabled via gservices.
+ //
+ // Note that we do not dexopt instant apps by default. dexopt can take some time to
+ // complete, so we skip this step during installation. Instead, we'll take extra time
+ // the first time the instant app starts. It's preferred to do it this way to provide
+ // continuous progress to the useur instead of mysteriously blocking somewhere in the
+ // middle of running an instant app. The default behaviour can be overridden
+ // via gservices.
+ final boolean performDexopt = !forwardLocked
+ && !pkg.applicationInfo.isExternalAsec()
+ && (!instantApp || Global.getInt(mContext.getContentResolver(),
+ Global.INSTANT_APP_DEXOPT_ENABLED, 0) != 0);
+
+ if (performDexopt) {
+ Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "dexopt");
+ // Do not run PackageDexOptimizer through the local performDexOpt
+ // method because `pkg` may not be in `mPackages` yet.
+ //
+ // Also, don't fail application installs if the dexopt step fails.
+ DexoptOptions dexoptOptions = new DexoptOptions(pkg.packageName,
+ REASON_INSTALL,
+ DexoptOptions.DEXOPT_BOOT_COMPLETE);
+ mPackageDexOptimizer.performDexOpt(pkg, pkg.usesLibraryFiles,
+ null /* instructionSets */,
+ getOrCreateCompilerPackageStats(pkg),
+ mDexManager.getPackageUseInfoOrDefault(pkg.packageName),
+ dexoptOptions);
+ Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
+ }
+
+ // Notify BackgroundDexOptService that the package has been changed.
+ // If this is an update of a package which used to fail to compile,
+ // BackgroundDexOptService will remove it from its blacklist.
+ // TODO: Layering violation
+ BackgroundDexOptService.notifyPackageChanged(pkg.packageName);
+
startIntentFilterVerifications(args.user.getIdentifier(), replace, pkg);
try (PackageFreezer freezer = freezePackageForInstall(pkgName, installFlags,
@@ -22536,7 +22570,8 @@
for (PackageParser.Package pkg : packages) {
ipw.println("[" + pkg.packageName + "]");
ipw.increaseIndent();
- mPackageDexOptimizer.dumpDexoptState(ipw, pkg);
+ mPackageDexOptimizer.dumpDexoptState(ipw, pkg,
+ mDexManager.getPackageUseInfoOrDefault(pkg.packageName));
ipw.decreaseIndent();
}
}
@@ -25039,6 +25074,29 @@
}
}
}
+
+ Set<String> getUnusedPackages(long downgradeTimeThresholdMillis) {
+ Set<String> unusedPackages = new HashSet<>();
+ long currentTimeInMillis = System.currentTimeMillis();
+ synchronized (mPackages) {
+ for (PackageParser.Package pkg : mPackages.values()) {
+ PackageSetting ps = mSettings.mPackages.get(pkg.packageName);
+ if (ps == null) {
+ continue;
+ }
+ PackageDexUsage.PackageUseInfo packageUseInfo =
+ getDexManager().getPackageUseInfoOrDefault(pkg.packageName);
+ if (PackageManagerServiceUtils
+ .isUnusedSinceTimeInMillis(ps.firstInstallTime, currentTimeInMillis,
+ downgradeTimeThresholdMillis, packageUseInfo,
+ pkg.getLatestPackageUseTimeInMills(),
+ pkg.getLatestForegroundPackageUseTimeInMills())) {
+ unusedPackages.add(pkg.packageName);
+ }
+ }
+ }
+ return unusedPackages;
+ }
}
interface PackageSender {
diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceCompilerMapping.java b/services/core/java/com/android/server/pm/PackageManagerServiceCompilerMapping.java
index ec248f5..19b0d9b 100644
--- a/services/core/java/com/android/server/pm/PackageManagerServiceCompilerMapping.java
+++ b/services/core/java/com/android/server/pm/PackageManagerServiceCompilerMapping.java
@@ -26,14 +26,19 @@
public class PackageManagerServiceCompilerMapping {
// Names for compilation reasons.
static final String REASON_STRINGS[] = {
- "first-boot", "boot", "install", "bg-dexopt", "ab-ota"
+ "first-boot", "boot", "install", "bg-dexopt", "ab-ota", "inactive", "shared"
};
+ static final int REASON_SHARED_INDEX = 6;
+
// Static block to ensure the strings array is of the right length.
static {
if (PackageManagerService.REASON_LAST + 1 != REASON_STRINGS.length) {
throw new IllegalStateException("REASON_STRINGS not correct");
}
+ if (!"shared".equals(REASON_STRINGS[REASON_SHARED_INDEX])) {
+ throw new IllegalStateException("REASON_STRINGS not correct because of shared index");
+ }
}
private static String getSystemPropertyName(int reason) {
@@ -52,11 +57,18 @@
!DexFile.isValidCompilerFilter(sysPropValue)) {
throw new IllegalStateException("Value \"" + sysPropValue +"\" not valid "
+ "(reason " + REASON_STRINGS[reason] + ")");
+ } else if (!isFilterAllowedForReason(reason, sysPropValue)) {
+ throw new IllegalStateException("Value \"" + sysPropValue +"\" not allowed "
+ + "(reason " + REASON_STRINGS[reason] + ")");
}
return sysPropValue;
}
+ private static boolean isFilterAllowedForReason(int reason, String filter) {
+ return reason != REASON_SHARED_INDEX || !DexFile.isProfileGuidedCompilerFilter(filter);
+ }
+
// Check that the properties are set and valid.
// Note: this is done in a separate method so this class can be statically initialized.
static void checkProperties() {
diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
index eb1e7bd..1e0ce7a 100644
--- a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
+++ b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
@@ -16,6 +16,9 @@
package com.android.server.pm;
+import com.android.server.pm.dex.DexManager;
+import com.android.server.pm.dex.PackageDexUsage;
+
import static com.android.server.pm.PackageManagerService.DEBUG_DEXOPT;
import static com.android.server.pm.PackageManagerService.TAG;
@@ -140,9 +143,11 @@
sortTemp, packageManagerService);
// Give priority to apps used by other apps.
+ DexManager dexManager = packageManagerService.getDexManager();
applyPackageFilter((pkg) ->
- packageManagerService.getDexManager().isUsedByOtherApps(pkg.packageName), result,
- remainingPkgs, sortTemp, packageManagerService);
+ dexManager.getPackageUseInfoOrDefault(pkg.packageName)
+ .isAnyCodePathUsedByOtherApps(),
+ result, remainingPkgs, sortTemp, packageManagerService);
// Filter out packages that aren't recently used, add all remaining apps.
// TODO: add a property to control this?
@@ -186,6 +191,41 @@
}
/**
+ * Checks if the package was inactive during since <code>thresholdTimeinMillis</code>.
+ * Package is considered active, if:
+ * 1) It was active in foreground.
+ * 2) It was active in background and also used by other apps.
+ *
+ * If it doesn't have sufficient information about the package, it return <code>false</code>.
+ */
+ static boolean isUnusedSinceTimeInMillis(long firstInstallTime, long currentTimeInMillis,
+ long thresholdTimeinMillis, PackageDexUsage.PackageUseInfo packageUseInfo,
+ long latestPackageUseTimeInMillis, long latestForegroundPackageUseTimeInMillis) {
+
+ if (currentTimeInMillis - firstInstallTime < thresholdTimeinMillis) {
+ return false;
+ }
+
+ // If the app was active in foreground during the threshold period.
+ boolean isActiveInForeground = (currentTimeInMillis
+ - latestForegroundPackageUseTimeInMillis)
+ < thresholdTimeinMillis;
+
+ if (isActiveInForeground) {
+ return false;
+ }
+
+ // If the app was active in background during the threshold period and was used
+ // by other packages.
+ boolean isActiveInBackgroundAndUsedByOtherPackages = ((currentTimeInMillis
+ - latestPackageUseTimeInMillis)
+ < thresholdTimeinMillis)
+ && packageUseInfo.isAnyCodePathUsedByOtherApps();
+
+ return !isActiveInBackgroundAndUsedByOtherPackages;
+ }
+
+ /**
* Returns the canonicalized path of {@code path} as per {@code realpath(3)}
* semantics.
*/
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index 20d7b28..10ceba4 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -365,6 +365,7 @@
String compilationReason = null;
String checkProfilesRaw = null;
boolean secondaryDex = false;
+ String split = null;
String opt;
while ((opt = getNextOption()) != null) {
@@ -395,6 +396,9 @@
case "--secondary-dex":
secondaryDex = true;
break;
+ case "--split":
+ split = getNextArgRequired();
+ break;
default:
pw.println("Error: Unknown option: " + opt);
return 1;
@@ -423,6 +427,16 @@
return 1;
}
+ if (allPackages && split != null) {
+ pw.println("-a cannot be specified together with --split");
+ return 1;
+ }
+
+ if (secondaryDex && split != null) {
+ pw.println("--secondary-dex cannot be specified together with --split");
+ return 1;
+ }
+
String targetCompilerFilter;
if (compilerFilter != null) {
if (!DexFile.isValidCompilerFilter(compilerFilter)) {
@@ -472,7 +486,7 @@
targetCompilerFilter, forceCompilation)
: mInterface.performDexOptMode(packageName,
checkProfiles, targetCompilerFilter, forceCompilation,
- true /* bootComplete */);
+ true /* bootComplete */, split);
if (!result) {
failedPackages.add(packageName);
}
@@ -1609,7 +1623,7 @@
pw.println(" help");
pw.println(" Print this help text.");
pw.println("");
- pw.println(" compile [-m MODE | -r REASON] [-f] [-c]");
+ pw.println(" compile [-m MODE | -r REASON] [-f] [-c] [--split SPLIT_NAME]");
pw.println(" [--reset] [--check-prof (true | false)] (-a | TARGET-PACKAGE)");
pw.println(" Trigger compilation of TARGET-PACKAGE or all packages if \"-a\".");
pw.println(" Options:");
@@ -1635,6 +1649,7 @@
pw.println(" --reset: restore package to its post-install state");
pw.println(" --check-prof (true | false): look at profiles when doing dexopt?");
pw.println(" --secondary-dex: compile app secondary dex files");
+ pw.println(" --split SPLIT: compile only the given split name");
pw.println(" bg-dexopt-job");
pw.println(" Execute the background optimizations immediately.");
pw.println(" Note that the command only runs the background optimizer logic. It may");
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 be50eee..6274754 100644
--- a/services/core/java/com/android/server/pm/dex/DexManager.java
+++ b/services/core/java/com/android/server/pm/dex/DexManager.java
@@ -36,6 +36,8 @@
import java.io.File;
import java.io.IOException;
+import java.util.Collection;
+import java.util.Collections;
import java.util.List;
import java.util.HashMap;
import java.util.HashSet;
@@ -81,6 +83,19 @@
private static int DEX_SEARCH_FOUND_SPLIT = 2; // dex file is a split apk
private static int DEX_SEARCH_FOUND_SECONDARY = 3; // dex file is a secondary dex
+ /**
+ * We do not record packages that have no secondary dex files or that are not used by other
+ * apps. This is an optimization to reduce the amount of data that needs to be written to
+ * disk (apps will not usually be shared so this trims quite a bit the number we record).
+ *
+ * To make this behaviour transparent to the callers which need use information on packages,
+ * DexManager will return this DEFAULT instance from
+ * {@link DexManager#getPackageUseInfoOrDefault}. It has no data about secondary dex files and
+ * is marked as not being used by other apps. This reflects the intended behaviour when we don't
+ * find the package in the underlying data file.
+ */
+ private final static PackageUseInfo DEFAULT_USE_INFO = new PackageUseInfo();
+
public DexManager(IPackageManager pms, PackageDexOptimizer pdo,
Installer installer, Object installLock) {
mPackageCodeLocationsCache = new HashMap<>();
@@ -97,29 +112,55 @@
* return as fast as possible.
*
* @param loadingAppInfo the package performing the load
- * @param dexPaths the list of dex files being loaded
+ * @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}.
+ * The dex files found in the first class path will be recorded in the usage file.
* @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> dexPaths,
- String loaderIsa, int loaderUserId) {
+ public void notifyDexLoad(ApplicationInfo loadingAppInfo, List<String> classLoadersNames,
+ List<String> classPaths, String loaderIsa, int loaderUserId) {
try {
- notifyDexLoadInternal(loadingAppInfo, dexPaths, loaderIsa, loaderUserId);
+ notifyDexLoadInternal(loadingAppInfo, classLoadersNames, classPaths, loaderIsa,
+ loaderUserId);
} catch (Exception e) {
Slog.w(TAG, "Exception while notifying dex load for package " +
loadingAppInfo.packageName, e);
}
}
- private void notifyDexLoadInternal(ApplicationInfo loadingAppInfo, List<String> dexPaths,
- String loaderIsa, int loaderUserId) {
+ private void notifyDexLoadInternal(ApplicationInfo loadingAppInfo,
+ List<String> classLoaderNames, List<String> classPaths, String loaderIsa,
+ int loaderUserId) {
+ if (classLoaderNames.size() != classPaths.size()) {
+ Slog.wtf(TAG, "Bad call to noitfyDexLoad: args have different size");
+ return;
+ }
+ if (classLoaderNames.isEmpty()) {
+ Slog.wtf(TAG, "Bad call to notifyDexLoad: class loaders list is empty");
+ return;
+ }
if (!PackageManagerServiceUtils.checkISA(loaderIsa)) {
- Slog.w(TAG, "Loading dex files " + dexPaths + " in unsupported ISA: " +
+ Slog.w(TAG, "Loading dex files " + classPaths + " in unsupported ISA: " +
loaderIsa + "?");
return;
}
- for (String dexPath : dexPaths) {
+ // The classpath is represented as a list of dex files separated by File.pathSeparator.
+ String[] dexPathsToRegister = classPaths.get(0).split(File.pathSeparator);
+
+ // Encode the class loader contexts for the dexPathsToRegister.
+ String[] classLoaderContexts = DexoptUtils.processContextForDexLoad(
+ classLoaderNames, classPaths);
+
+ int dexPathIndex = 0;
+ for (String dexPath : dexPathsToRegister) {
// Find the owning package name.
DexSearchResult searchResult = getDexPackage(loadingAppInfo, dexPath, loaderUserId);
@@ -147,23 +188,25 @@
// Record dex file usage. If the current usage is a new pattern (e.g. new secondary,
// or UsedBytOtherApps), 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.
+
+ // A null classLoaderContexts means that there are unsupported class loaders in the
+ // chain.
+ String classLoaderContext = classLoaderContexts == null
+ ? PackageDexUsage.UNSUPPORTED_CLASS_LOADER_CONTEXT
+ : classLoaderContexts[dexPathIndex];
if (mPackageDexUsage.record(searchResult.mOwningPackageName,
- dexPath, loaderUserId, loaderIsa, isUsedByOtherApps, primaryOrSplit)) {
+ dexPath, loaderUserId, loaderIsa, isUsedByOtherApps, primaryOrSplit,
+ loadingAppInfo.packageName, classLoaderContext)) {
mPackageDexUsage.maybeWriteAsync();
}
} else {
- // This can happen in a few situations:
- // - bogus dex loads
- // - recent installs/uninstalls that we didn't detect.
- // - new installed splits
// If we can't find the owner of the dex we simply do not track it. The impact is
// that the dex file will not be considered for offline optimizations.
- // TODO(calin): add hooks for move/uninstall notifications to
- // capture package moves or obsolete packages.
if (DEBUG) {
Slog.i(TAG, "Could not find owning package for dex file: " + dexPath);
}
}
+ dexPathIndex++;
}
}
@@ -269,6 +312,8 @@
private void loadInternal(Map<Integer, List<PackageInfo>> existingPackages) {
Map<String, Set<Integer>> packageToUsersMap = new HashMap<>();
+ Map<String, Set<String>> packageToCodePaths = new HashMap<>();
+
// Cache the code locations for the installed packages. This allows for
// faster lookups (no locks) when finding what package owns the dex file.
for (Map.Entry<Integer, List<PackageInfo>> entry : existingPackages.entrySet()) {
@@ -278,54 +323,72 @@
// Cache the code locations.
cachePackageInfo(pi, userId);
- // Cache a map from package name to the set of user ids who installed the package.
+ // Cache two maps:
+ // - from package name to the set of user ids who installed the package.
+ // - from package name to the set of code paths.
// We will use it to sync the data and remove obsolete entries from
// mPackageDexUsage.
Set<Integer> users = putIfAbsent(
packageToUsersMap, pi.packageName, new HashSet<>());
users.add(userId);
+
+ Set<String> codePaths = putIfAbsent(
+ packageToCodePaths, pi.packageName, new HashSet<>());
+ codePaths.add(pi.applicationInfo.sourceDir);
+ if (pi.applicationInfo.splitSourceDirs != null) {
+ Collections.addAll(codePaths, pi.applicationInfo.splitSourceDirs);
+ }
}
}
mPackageDexUsage.read();
- mPackageDexUsage.syncData(packageToUsersMap);
+ mPackageDexUsage.syncData(packageToUsersMap, packageToCodePaths);
}
/**
* Get the package dex usage for the given package name.
- * @return the package data or null if there is no data available for this package.
+ * If there is no usage info the method will return a default {@code PackageUseInfo} with
+ * no data about secondary dex files and marked as not being used by other apps.
+ *
+ * Note that no use info means the package was not used or it was used but not by other apps.
+ * Also, note that right now we might prune packages which are not used by other apps.
+ * TODO(calin): maybe we should not (prune) so we can have an accurate view when we try
+ * to access the package use.
*/
- public PackageUseInfo getPackageUseInfo(String packageName) {
- return mPackageDexUsage.getPackageUseInfo(packageName);
+ public PackageUseInfo getPackageUseInfoOrDefault(String packageName) {
+ PackageUseInfo useInfo = mPackageDexUsage.getPackageUseInfo(packageName);
+ return useInfo == null ? DEFAULT_USE_INFO : useInfo;
}
/**
- * Perform dexopt on the package {@code packageName} secondary dex files.
- * @return true if all secondary dex files were processed successfully (compiled or skipped
- * because they don't need to be compiled)..
+ * Return whether or not the manager has usage information on the give package.
+ *
+ * Note that no use info means the package was not used or it was used but not by other apps.
+ * Also, note that right now we might prune packages which are not used by other apps.
+ * TODO(calin): maybe we should not (prune) so we can have an accurate view when we try
+ * to access the package use.
*/
- public boolean dexoptSecondaryDex(String packageName, int compilerReason, boolean force) {
- return dexoptSecondaryDex(packageName,
- PackageManagerServiceCompilerMapping.getCompilerFilterForReason(compilerReason),
- force);
+ /*package*/ boolean hasInfoOnPackage(String packageName) {
+ return mPackageDexUsage.getPackageUseInfo(packageName) != null;
}
/**
- * Perform dexopt on the package {@code packageName} secondary dex files.
+ * Perform dexopt on with the given {@code options} on the secondary dex files.
* @return true if all secondary dex files were processed successfully (compiled or skipped
* because they don't need to be compiled)..
*/
- public boolean dexoptSecondaryDex(String packageName, String compilerFilter, boolean force) {
+ public boolean dexoptSecondaryDex(DexoptOptions options) {
// Select the dex optimizer based on the force parameter.
// Forced compilation is done through ForcedUpdatePackageDexOptimizer which will adjust
// the necessary dexopt flags to make sure that compilation is not skipped. This avoid
// passing the force flag through the multitude of layers.
// Note: The force option is rarely used (cmdline input for testing, mostly), so it's OK to
// allocate an object here.
- PackageDexOptimizer pdo = force
+ PackageDexOptimizer pdo = options.isForce()
? new PackageDexOptimizer.ForcedUpdatePackageDexOptimizer(mPackageDexOptimizer)
: mPackageDexOptimizer;
- PackageUseInfo useInfo = getPackageUseInfo(packageName);
+ String packageName = options.getPackageName();
+ PackageUseInfo useInfo = getPackageUseInfoOrDefault(packageName);
if (useInfo == null || useInfo.getDexUseInfoMap().isEmpty()) {
if (DEBUG) {
Slog.d(TAG, "No secondary dex use for package:" + packageName);
@@ -337,7 +400,8 @@
for (Map.Entry<String, DexUseInfo> entry : useInfo.getDexUseInfoMap().entrySet()) {
String dexPath = entry.getKey();
DexUseInfo dexUseInfo = entry.getValue();
- PackageInfo pkg = null;
+
+ PackageInfo pkg;
try {
pkg = mPackageManager.getPackageInfo(packageName, /*flags*/0,
dexUseInfo.getOwnerUserId());
@@ -356,7 +420,7 @@
}
int result = pdo.dexOptSecondaryDexPath(pkg.applicationInfo, dexPath,
- dexUseInfo.getLoaderIsas(), compilerFilter, dexUseInfo.isUsedByOtherApps());
+ dexUseInfo, options);
success = success && (result != PackageDexOptimizer.DEX_OPT_FAILED);
}
return success;
@@ -368,7 +432,7 @@
* deleted, update the internal records and delete any generated oat files.
*/
public void reconcileSecondaryDexFiles(String packageName) {
- PackageUseInfo useInfo = getPackageUseInfo(packageName);
+ PackageUseInfo useInfo = getPackageUseInfoOrDefault(packageName);
if (useInfo == null || useInfo.getDexUseInfoMap().isEmpty()) {
if (DEBUG) {
Slog.d(TAG, "No secondary dex use for package:" + packageName);
@@ -439,6 +503,8 @@
}
}
+ // TODO(calin): questionable API in the presence of class loaders context. Needs amends as the
+ // compilation happening here will use a pessimistic context.
public RegisterDexModuleResult registerDexModule(ApplicationInfo info, String dexPath,
boolean isUsedByOtherApps, int userId) {
// Find the owning package record.
@@ -457,11 +523,11 @@
// We found the package. Now record the usage for all declared ISAs.
boolean update = false;
- Set<String> isas = new HashSet<>();
for (String isa : getAppDexInstructionSets(info)) {
- isas.add(isa);
boolean newUpdate = mPackageDexUsage.record(searchResult.mOwningPackageName,
- dexPath, userId, isa, isUsedByOtherApps, /*primaryOrSplit*/ false);
+ dexPath, userId, isa, isUsedByOtherApps, /*primaryOrSplit*/ false,
+ searchResult.mOwningPackageName,
+ PackageDexUsage.UNKNOWN_CLASS_LOADER_CONTEXT);
update |= newUpdate;
}
if (update) {
@@ -471,8 +537,13 @@
// Try to optimize the package according to the install reason.
String compilerFilter = PackageManagerServiceCompilerMapping.getCompilerFilterForReason(
PackageManagerService.REASON_INSTALL);
- int result = mPackageDexOptimizer.dexOptSecondaryDexPath(info, dexPath, isas,
- compilerFilter, isUsedByOtherApps);
+ DexUseInfo dexUseInfo = mPackageDexUsage.getPackageUseInfo(searchResult.mOwningPackageName)
+ .getDexUseInfoMap().get(dexPath);
+
+ DexoptOptions options = new DexoptOptions(info.packageName, compilerFilter, /*flags*/0);
+
+ int result = mPackageDexOptimizer.dexOptSecondaryDexPath(info, dexPath, dexUseInfo,
+ options);
// If we fail to optimize the package log an error but don't propagate the error
// back to the app. The app cannot do much about it and the background job
@@ -493,23 +564,6 @@
}
/**
- * Return true if the profiling data collected for the given app indicate
- * that the apps's APK has been loaded by another app.
- * Note that this returns false for all apps without any collected profiling data.
- */
- public boolean isUsedByOtherApps(String packageName) {
- PackageUseInfo useInfo = getPackageUseInfo(packageName);
- if (useInfo == null) {
- // No use info, means the package was not used or it was used but not by other apps.
- // Note that right now we might prune packages which are not used by other apps.
- // TODO(calin): maybe we should not (prune) so we can have an accurate view when we try
- // to access the package use.
- return false;
- }
- return useInfo.isUsedByOtherApps();
- }
-
- /**
* Retrieves the package which owns the given dexPath.
*/
private DexSearchResult getDexPackage(
@@ -566,6 +620,13 @@
return existingValue == null ? newValue : existingValue;
}
+ /**
+ * Writes the in-memory package dex usage to disk right away.
+ */
+ public void writePackageDexUsageNow() {
+ mPackageDexUsage.writeNow();
+ }
+
public static class RegisterDexModuleResult {
public RegisterDexModuleResult() {
this(false, null);
diff --git a/services/core/java/com/android/server/pm/dex/DexoptOptions.java b/services/core/java/com/android/server/pm/dex/DexoptOptions.java
new file mode 100644
index 0000000..0966770
--- /dev/null
+++ b/services/core/java/com/android/server/pm/dex/DexoptOptions.java
@@ -0,0 +1,151 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.pm.dex;
+
+import static com.android.server.pm.PackageManagerServiceCompilerMapping.getCompilerFilterForReason;
+
+import android.annotation.Nullable;
+
+/**
+ * Options used for dexopt invocations.
+ */
+public final class DexoptOptions {
+ // When set, the profiles will be checked for updates before calling dexopt. If
+ // the apps profiles didn't update in a meaningful way (decided by the compiler), dexopt
+ // will be skipped.
+ // Currently this only affects the optimization of primary apks. Secondary dex files
+ // will always check the profiles for updates.
+ public static final int DEXOPT_CHECK_FOR_PROFILES_UPDATES = 1 << 0;
+
+ // When set, dexopt will execute unconditionally (even if not needed).
+ public static final int DEXOPT_FORCE = 1 << 1;
+
+ // Whether or not the invocation of dexopt is done after the boot is completed. This is used
+ // in order to adjust the priority of the compilation thread.
+ public static final int DEXOPT_BOOT_COMPLETE = 1 << 2;
+
+ // When set, the dexopt invocation will optimize only the secondary dex files. If false, dexopt
+ // will only consider the primary apk.
+ public static final int DEXOPT_ONLY_SECONDARY_DEX = 1 << 3;
+
+ // When set, dexopt will optimize only dex files that are used by other apps.
+ // Currently, this flag is ignored for primary apks.
+ public static final int DEXOPT_ONLY_SHARED_DEX = 1 << 4;
+
+ // When set, dexopt will attempt to scale down the optimizations previously applied in order
+ // save disk space.
+ public static final int DEXOPT_DOWNGRADE = 1 << 5;
+
+ // When set, dexopt will compile the dex file as a shared library even if it is not actually
+ // used by other apps. This is used to force the compilation or shared libraries declared
+ // with in the manifest with ''uses-library' before we have a chance to detect they are
+ // actually shared at runtime.
+ public static final int DEXOPT_AS_SHARED_LIBRARY = 1 << 6;
+
+ // When set, indicates that dexopt is invoked from the background service.
+ public static final int DEXOPT_IDLE_BACKGROUND_JOB = 1 << 9;
+
+ // The name of package to optimize.
+ private final String mPackageName;
+
+ // The intended target compiler filter. Note that dexopt might adjust the filter before the
+ // execution based on factors like: vmSafeMode and packageUsedByOtherApps.
+ private final String mCompilerFilter;
+
+ // The set of flags for the dexopt options. It's a mix of the DEXOPT_* flags.
+ private final int mFlags;
+
+ // When not null, dexopt will optimize only the split identified by this name.
+ // It only applies for primary apk and it's always null if mOnlySecondaryDex is true.
+ private final String mSplitName;
+
+ public DexoptOptions(String packageName, String compilerFilter, int flags) {
+ this(packageName, compilerFilter, /*splitName*/ null, flags);
+ }
+
+ public DexoptOptions(String packageName, int compilerReason, int flags) {
+ this(packageName, getCompilerFilterForReason(compilerReason), flags);
+ }
+
+ public DexoptOptions(String packageName, String compilerFilter, String splitName, int flags) {
+ int validityMask =
+ DEXOPT_CHECK_FOR_PROFILES_UPDATES |
+ DEXOPT_FORCE |
+ DEXOPT_BOOT_COMPLETE |
+ DEXOPT_ONLY_SECONDARY_DEX |
+ DEXOPT_ONLY_SHARED_DEX |
+ DEXOPT_DOWNGRADE |
+ DEXOPT_AS_SHARED_LIBRARY |
+ DEXOPT_IDLE_BACKGROUND_JOB;
+ if ((flags & (~validityMask)) != 0) {
+ throw new IllegalArgumentException("Invalid flags : " + Integer.toHexString(flags));
+ }
+
+ mPackageName = packageName;
+ mCompilerFilter = compilerFilter;
+ mFlags = flags;
+ mSplitName = splitName;
+ }
+
+ public String getPackageName() {
+ return mPackageName;
+ }
+
+ public boolean isCheckForProfileUpdates() {
+ return (mFlags & DEXOPT_CHECK_FOR_PROFILES_UPDATES) != 0;
+ }
+
+ public String getCompilerFilter() {
+ return mCompilerFilter;
+ }
+
+ public boolean isForce() {
+ return (mFlags & DEXOPT_FORCE) != 0;
+ }
+
+ public boolean isBootComplete() {
+ return (mFlags & DEXOPT_BOOT_COMPLETE) != 0;
+ }
+
+ public boolean isDexoptOnlySecondaryDex() {
+ return (mFlags & DEXOPT_ONLY_SECONDARY_DEX) != 0;
+ }
+
+ public boolean isDexoptOnlySharedDex() {
+ return (mFlags & DEXOPT_ONLY_SHARED_DEX) != 0;
+ }
+
+ public boolean isDowngrade() {
+ return (mFlags & DEXOPT_DOWNGRADE) != 0;
+ }
+
+ public boolean isDexoptAsSharedLibrary() {
+ return (mFlags & DEXOPT_AS_SHARED_LIBRARY) != 0;
+ }
+
+ public boolean isDexoptIdleBackgroundJob() {
+ return (mFlags & DEXOPT_IDLE_BACKGROUND_JOB) != 0;
+ }
+
+ public String getSplitName() {
+ return mSplitName;
+ }
+
+ public int getFlags() {
+ return mFlags;
+ }
+}
diff --git a/services/core/java/com/android/server/pm/dex/DexoptUtils.java b/services/core/java/com/android/server/pm/dex/DexoptUtils.java
new file mode 100644
index 0000000..c23b031
--- /dev/null
+++ b/services/core/java/com/android/server/pm/dex/DexoptUtils.java
@@ -0,0 +1,369 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.pm.dex;
+
+import android.content.pm.ApplicationInfo;
+import android.util.Slog;
+import android.util.SparseArray;
+
+import com.android.server.pm.PackageDexOptimizer;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+public final class DexoptUtils {
+ private static final String TAG = "DexoptUtils";
+
+ private DexoptUtils() {}
+
+ /**
+ * Creates the class loader context dependencies for each of the application code paths.
+ * The returned array contains the class loader contexts that needs to be passed to dexopt in
+ * order to ensure correct optimizations. "Code" paths with no actual code, as specified by
+ * {@param pathsWithCode}, are ignored and will have null as their context in the returned array
+ * (configuration splits are an example of paths without code).
+ *
+ * A class loader context describes how the class loader chain should be built by dex2oat
+ * in order to ensure that classes are resolved during compilation as they would be resolved
+ * at runtime. The context will be encoded in the compiled code. If at runtime the dex file is
+ * loaded in a different context (with a different set of class loaders or a different
+ * classpath), the compiled code will be rejected.
+ *
+ * Note that the class loader context only includes dependencies and not the code path itself.
+ * The contexts are created based on the application split dependency list and
+ * the provided shared libraries.
+ *
+ * All the code paths encoded in the context will be relative to the base directory. This
+ * enables stage compilation where compiler artifacts may be moved around.
+ *
+ * The result is indexed as follows:
+ * - index 0 contains the context for the base apk
+ * - index 1 to n contain the context for the splits in the order determined by
+ * {@code info.getSplitCodePaths()}
+ *
+ * IMPORTANT: keep this logic in sync with the loading code in {@link android.app.LoadedApk}
+ * and pay attention to the way the classpath is created for the non isolated mode in:
+ * {@link android.app.LoadedApk#makePaths(
+ * android.app.ActivityThread, boolean, ApplicationInfo, List, List)}.
+ */
+ public static String[] getClassLoaderContexts(ApplicationInfo info,
+ String[] sharedLibraries, boolean[] pathsWithCode) {
+ // The base class loader context contains only the shared library.
+ String sharedLibrariesClassPath = encodeClasspath(sharedLibraries);
+ String baseApkContextClassLoader = encodeClassLoader(
+ sharedLibrariesClassPath, "dalvik.system.PathClassLoader");
+
+ if (info.getSplitCodePaths() == null) {
+ // The application has no splits.
+ return new String[] {baseApkContextClassLoader};
+ }
+
+ // The application has splits. Compute their class loader contexts.
+
+ // First, cache the relative paths of the splits and do some sanity checks
+ String[] splitRelativeCodePaths = getSplitRelativeCodePaths(info);
+
+ // The splits have an implicit dependency on the base apk.
+ // This means that we have to add the base apk file in addition to the shared libraries.
+ String baseApkName = new File(info.getBaseCodePath()).getName();
+ String sharedLibrariesAndBaseClassPath =
+ encodeClasspath(sharedLibrariesClassPath, baseApkName);
+
+ // The result is stored in classLoaderContexts.
+ // Index 0 is the class loaded context for the base apk.
+ // Index `i` is the class loader context encoding for split `i`.
+ String[] classLoaderContexts = new String[/*base apk*/ 1 + splitRelativeCodePaths.length];
+ classLoaderContexts[0] = pathsWithCode[0] ? baseApkContextClassLoader : null;
+
+ if (!info.requestsIsolatedSplitLoading() || info.splitDependencies == null) {
+ // If the app didn't request for the splits to be loaded in isolation or if it does not
+ // declare inter-split dependencies, then all the splits will be loaded in the base
+ // apk class loader (in the order of their definition).
+ String classpath = sharedLibrariesAndBaseClassPath;
+ for (int i = 1; i < classLoaderContexts.length; i++) {
+ classLoaderContexts[i] = pathsWithCode[i]
+ ? encodeClassLoader(classpath, "dalvik.system.PathClassLoader") : null;
+ // Note that the splits with no code are not removed from the classpath computation.
+ // i.e. split_n might get the split_n-1 in its classpath dependency even
+ // if split_n-1 has no code.
+ // The splits with no code do not matter for the runtime which ignores
+ // apks without code when doing the classpath checks. As such we could actually
+ // filter them but we don't do it in order to keep consistency with how the apps
+ // are loaded.
+ classpath = encodeClasspath(classpath, splitRelativeCodePaths[i - 1]);
+ }
+ } else {
+ // In case of inter-split dependencies, we need to walk the dependency chain of each
+ // split. We do this recursively and store intermediate results in classLoaderContexts.
+
+ // First, look at the split class loaders and cache their individual contexts (i.e.
+ // the class loader + the name of the split). This is an optimization to avoid
+ // re-computing them during the recursive call.
+ // The cache is stored in splitClassLoaderEncodingCache. The difference between this and
+ // classLoaderContexts is that the later contains the full chain of class loaders for
+ // a given split while splitClassLoaderEncodingCache only contains a single class loader
+ // encoding.
+ String[] splitClassLoaderEncodingCache = new String[splitRelativeCodePaths.length];
+ for (int i = 0; i < splitRelativeCodePaths.length; i++) {
+ splitClassLoaderEncodingCache[i] = encodeClassLoader(splitRelativeCodePaths[i],
+ "dalvik.system.PathClassLoader");
+ }
+ String splitDependencyOnBase = encodeClassLoader(
+ sharedLibrariesAndBaseClassPath, "dalvik.system.PathClassLoader");
+ SparseArray<int[]> splitDependencies = info.splitDependencies;
+
+ // Note that not all splits have dependencies (e.g. configuration splits)
+ // The splits without dependencies will have classLoaderContexts[config_split_index]
+ // set to null after this step.
+ for (int i = 1; i < splitDependencies.size(); i++) {
+ int splitIndex = splitDependencies.keyAt(i);
+ if (pathsWithCode[splitIndex]) {
+ // Compute the class loader context only for the splits with code.
+ getParentDependencies(splitIndex, splitClassLoaderEncodingCache,
+ splitDependencies, classLoaderContexts, splitDependencyOnBase);
+ }
+ }
+
+ // At this point classLoaderContexts contains only the parent dependencies.
+ // We also need to add the class loader of the current split which should
+ // come first in the context.
+ for (int i = 1; i < classLoaderContexts.length; i++) {
+ String splitClassLoader = encodeClassLoader("", "dalvik.system.PathClassLoader");
+ if (pathsWithCode[i]) {
+ // If classLoaderContexts[i] is null it means that the split does not have
+ // any dependency. In this case its context equals its declared class loader.
+ classLoaderContexts[i] = classLoaderContexts[i] == null
+ ? splitClassLoader
+ : encodeClassLoaderChain(splitClassLoader, classLoaderContexts[i]);
+ } else {
+ // This is a split without code, it has no dependency and it is not compiled.
+ // Its context will be null.
+ classLoaderContexts[i] = null;
+ }
+ }
+ }
+
+ return classLoaderContexts;
+ }
+
+ /**
+ * Recursive method to generate the class loader context dependencies for the split with the
+ * given index. {@param classLoaderContexts} acts as an accumulator. Upton return
+ * {@code classLoaderContexts[index]} will contain the split dependency.
+ * During computation, the method may resolve the dependencies of other splits as it traverses
+ * the entire parent chain. The result will also be stored in {@param classLoaderContexts}.
+ *
+ * Note that {@code index 0} denotes the base apk and it is special handled. When the
+ * recursive call hits {@code index 0} the method returns {@code splitDependencyOnBase}.
+ * {@code classLoaderContexts[0]} is not modified in this method.
+ *
+ * @param index the index of the split (Note that index 0 denotes the base apk)
+ * @param splitClassLoaderEncodingCache the class loader encoding for the individual splits.
+ * It contains only the split class loader and not the the base. The split
+ * with {@code index} has its context at {@code splitClassLoaderEncodingCache[index - 1]}.
+ * @param splitDependencies the dependencies for all splits. Note that in this array index 0
+ * is the base and splits start from index 1.
+ * @param classLoaderContexts the result accumulator. index 0 is the base and never set. Splits
+ * start at index 1.
+ * @param splitDependencyOnBase the encoding of the implicit split dependency on base.
+ */
+ private static String getParentDependencies(int index, String[] splitClassLoaderEncodingCache,
+ SparseArray<int[]> splitDependencies, String[] classLoaderContexts,
+ String splitDependencyOnBase) {
+ // If we hit the base apk return its custom dependency list which is
+ // sharedLibraries + base.apk
+ if (index == 0) {
+ return splitDependencyOnBase;
+ }
+ // Return the result if we've computed the splitDependencies for this index already.
+ if (classLoaderContexts[index] != null) {
+ return classLoaderContexts[index];
+ }
+ // Get the splitDependencies for the parent of this index and append its path to it.
+ int parent = splitDependencies.get(index)[0];
+ String parentDependencies = getParentDependencies(parent, splitClassLoaderEncodingCache,
+ splitDependencies, classLoaderContexts, splitDependencyOnBase);
+
+ // The split context is: `parent context + parent dependencies context`.
+ String splitContext = (parent == 0) ?
+ parentDependencies :
+ encodeClassLoaderChain(splitClassLoaderEncodingCache[parent - 1], parentDependencies);
+ classLoaderContexts[index] = splitContext;
+ return splitContext;
+ }
+
+ /**
+ * Encodes the shared libraries classpathElements in a format accepted by dexopt.
+ * NOTE: Keep this in sync with the dexopt expectations! Right now that is
+ * a list separated by ':'.
+ */
+ private static String encodeClasspath(String[] classpathElements) {
+ if (classpathElements == null || classpathElements.length == 0) {
+ return "";
+ }
+ StringBuilder sb = new StringBuilder();
+ for (String element : classpathElements) {
+ if (sb.length() != 0) {
+ sb.append(":");
+ }
+ sb.append(element);
+ }
+ return sb.toString();
+ }
+
+ /**
+ * Adds an element to the encoding of an existing classpath.
+ * {@see PackageDexOptimizer.encodeClasspath(String[])}
+ */
+ private static String encodeClasspath(String classpath, String newElement) {
+ return classpath.isEmpty() ? newElement : (classpath + ":" + newElement);
+ }
+
+ /**
+ * Encodes a single class loader dependency starting from {@param path} and
+ * {@param classLoaderName}.
+ * When classpath is {@link PackageDexOptimizer#SKIP_SHARED_LIBRARY_CHECK}, the method returns
+ * the same. This special property is used only during OTA.
+ * NOTE: Keep this in sync with the dexopt expectations! Right now that is either "PCL[path]"
+ * for a PathClassLoader or "DLC[path]" for a DelegateLastClassLoader.
+ */
+ /*package*/ static String encodeClassLoader(String classpath, String classLoaderName) {
+ if (classpath.equals(PackageDexOptimizer.SKIP_SHARED_LIBRARY_CHECK)) {
+ return classpath;
+ }
+ String classLoaderDexoptEncoding = classLoaderName;
+ if ("dalvik.system.PathClassLoader".equals(classLoaderName)) {
+ classLoaderDexoptEncoding = "PCL";
+ } else {
+ Slog.wtf(TAG, "Unsupported classLoaderName: " + classLoaderName);
+ }
+ return classLoaderDexoptEncoding + "[" + classpath + "]";
+ }
+
+ /**
+ * Links to dependencies together in a format accepted by dexopt.
+ * For the special case when either of cl1 or cl2 equals
+ * {@link PackageDexOptimizer#SKIP_SHARED_LIBRARY_CHECK}, the method returns the same. This
+ * property is used only during OTA.
+ * NOTE: Keep this in sync with the dexopt expectations! Right now that is a list of split
+ * dependencies {@see encodeClassLoader} separated by ';'.
+ */
+ /*package*/ static String encodeClassLoaderChain(String cl1, String cl2) {
+ if (cl1.equals(PackageDexOptimizer.SKIP_SHARED_LIBRARY_CHECK) ||
+ cl2.equals(PackageDexOptimizer.SKIP_SHARED_LIBRARY_CHECK)) {
+ return PackageDexOptimizer.SKIP_SHARED_LIBRARY_CHECK;
+ }
+ if (cl1.isEmpty()) return cl2;
+ if (cl2.isEmpty()) return cl1;
+ return cl1 + ";" + cl2;
+ }
+
+ /**
+ * Compute the class loader context for the dex files present in the classpath of the first
+ * class loader from the given list (referred in the code as the {@code loadingClassLoader}).
+ * Each dex files gets its own class loader context in the returned array.
+ *
+ * Example:
+ * If classLoadersNames = {"dalvik.system.DelegateLastClassLoader",
+ * "dalvik.system.PathClassLoader"} and classPaths = {"foo.dex:bar.dex", "other.dex"}
+ * The output will be
+ * {"DLC[];PCL[other.dex]", "DLC[foo.dex];PCL[other.dex]"}
+ * with "DLC[];PCL[other.dex]" being the context for "foo.dex"
+ * and "DLC[foo.dex];PCL[other.dex]" the context for "bar.dex".
+ *
+ * If any of the class loaders names is unsupported the method will return null.
+ *
+ * The argument lists must be non empty and of the same size.
+ *
+ * @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.
+ * @param classPaths the class paths for the elements of {@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}.
+ * The return context will be for the dex files found in the first class path.
+ */
+ /*package*/ static String[] processContextForDexLoad(List<String> classLoadersNames,
+ List<String> classPaths) {
+ if (classLoadersNames.size() != classPaths.size()) {
+ throw new IllegalArgumentException(
+ "The size of the class loader names and the dex paths do not match.");
+ }
+ if (classLoadersNames.isEmpty()) {
+ throw new IllegalArgumentException("Empty classLoadersNames");
+ }
+
+ // Compute the context for the parent class loaders.
+ String parentContext = "";
+ // We know that these lists are actually ArrayLists so getting the elements by index
+ // is fine (they come over binder). Even if something changes we expect the sizes to be
+ // very small and it shouldn't matter much.
+ for (int i = 1; i < classLoadersNames.size(); i++) {
+ if (!isValidClassLoaderName(classLoadersNames.get(i))) {
+ return null;
+ }
+ String classpath = encodeClasspath(classPaths.get(i).split(File.pathSeparator));
+ parentContext = encodeClassLoaderChain(parentContext,
+ encodeClassLoader(classpath, classLoadersNames.get(i)));
+ }
+
+ // Now compute the class loader context for each dex file from the first classpath.
+ String loadingClassLoader = classLoadersNames.get(0);
+ if (!isValidClassLoaderName(loadingClassLoader)) {
+ return null;
+ }
+ String[] loadedDexPaths = classPaths.get(0).split(File.pathSeparator);
+ String[] loadedDexPathsContext = new String[loadedDexPaths.length];
+ String currentLoadedDexPathClasspath = "";
+ for (int i = 0; i < loadedDexPaths.length; i++) {
+ String dexPath = loadedDexPaths[i];
+ String currentContext = encodeClassLoader(
+ currentLoadedDexPathClasspath, loadingClassLoader);
+ loadedDexPathsContext[i] = encodeClassLoaderChain(currentContext, parentContext);
+ currentLoadedDexPathClasspath = encodeClasspath(currentLoadedDexPathClasspath, dexPath);
+ }
+ return loadedDexPathsContext;
+ }
+
+ // AOSP-only hack.
+ private static boolean isValidClassLoaderName(String name) {
+ return "dalvik.system.PathClassLoader".equals(name) || "dalvik.system.DexClassLoader".equals(name);
+ }
+
+ /**
+ * Returns the relative paths of the splits declared by the application {@code info}.
+ * Assumes that the application declares a non-null array of splits.
+ */
+ private static String[] getSplitRelativeCodePaths(ApplicationInfo info) {
+ String baseCodePath = new File(info.getBaseCodePath()).getParent();
+ String[] splitCodePaths = info.getSplitCodePaths();
+ String[] splitRelativeCodePaths = new String[splitCodePaths.length];
+ for (int i = 0; i < splitCodePaths.length; i++) {
+ File pathFile = new File(splitCodePaths[i]);
+ splitRelativeCodePaths[i] = pathFile.getName();
+ // Sanity check that the base paths of the splits are all the same.
+ String basePath = pathFile.getParent();
+ if (!basePath.equals(baseCodePath)) {
+ Slog.wtf(TAG, "Split paths have different base paths: " + basePath + " and " +
+ baseCodePath);
+ }
+ }
+ return splitRelativeCodePaths;
+ }
+}
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 8a66f12..a4a0a54 100644
--- a/services/core/java/com/android/server/pm/dex/PackageDexUsage.java
+++ b/services/core/java/com/android/server/pm/dex/PackageDexUsage.java
@@ -26,7 +26,6 @@
import com.android.server.pm.PackageManagerServiceUtils;
import java.io.BufferedReader;
-import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.InputStreamReader;
@@ -35,14 +34,19 @@
import java.io.Reader;
import java.io.StringWriter;
import java.io.Writer;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
import java.util.Iterator;
import java.util.HashMap;
import java.util.HashSet;
+import java.util.List;
import java.util.Map;
import java.util.Set;
import dalvik.system.VMRuntime;
import libcore.io.IoUtils;
+import libcore.util.Objects;
/**
* Stat file which store usage information about dex files.
@@ -50,19 +54,44 @@
public class PackageDexUsage extends AbstractStatsBase<Void> {
private final static String TAG = "PackageDexUsage";
- private final static int PACKAGE_DEX_USAGE_VERSION = 1;
+ // We support previous version to ensure that the usage list remains valid cross OTAs.
+ private final static int PACKAGE_DEX_USAGE_SUPPORTED_VERSION_1 = 1;
+ // Version 2 added:
+ // - the list of packages that load the dex files
+ // - class loader contexts for secondary dex files
+ // - usage for all code paths (including splits)
+ private final static int PACKAGE_DEX_USAGE_SUPPORTED_VERSION_2 = 2;
+
+ private final static int PACKAGE_DEX_USAGE_VERSION = PACKAGE_DEX_USAGE_SUPPORTED_VERSION_2;
+
private final static String PACKAGE_DEX_USAGE_VERSION_HEADER =
"PACKAGE_MANAGER__PACKAGE_DEX_USAGE__";
private final static String SPLIT_CHAR = ",";
+ private final static String CODE_PATH_LINE_CHAR = "+";
private final static String DEX_LINE_CHAR = "#";
+ private final static String LOADING_PACKAGE_CHAR = "@";
+
+ // One of the things we record about dex files is the class loader context that was used to
+ // load them. That should be stable but if it changes we don't keep track of variable contexts.
+ // Instead we put a special marker in the dex usage file in order to recognize the case and
+ // skip optimizations on that dex files.
+ /*package*/ static final String VARIABLE_CLASS_LOADER_CONTEXT =
+ "=VariableClassLoaderContext=";
+ // The marker used for unsupported class loader contexts.
+ /*package*/ static final String UNSUPPORTED_CLASS_LOADER_CONTEXT =
+ "=UnsupportedClassLoaderContext=";
+ // The markers used for unknown class loader contexts. This can happen if the dex file was
+ // recorded in a previous version and we didn't have a chance to update its usage.
+ /*package*/ static final String UNKNOWN_CLASS_LOADER_CONTEXT =
+ "=UnknownClassLoaderContext=";
// Map which structures the information we have on a package.
// Maps package name to package data (which stores info about UsedByOtherApps and
// secondary dex files.).
// Access to this map needs synchronized.
@GuardedBy("mPackageUseInfoMap")
- private Map<String, PackageUseInfo> mPackageUseInfoMap;
+ private final Map<String, PackageUseInfo> mPackageUseInfoMap;
public PackageDexUsage() {
super("package-dex-usage.list", "PackageDexUsage_DiskWriter", /*lock*/ false);
@@ -75,21 +104,28 @@
* Note this is called when apps load dex files and as such it should return
* as fast as possible.
*
- * @param loadingPackage the package performing the load
+ * @param owningPackageName the package owning the dex path
* @param dexPath the path of the dex files being loaded
* @param ownerUserId the user id which runs the code loading the dex files
* @param loaderIsa the ISA of the app loading the dex files
* @param isUsedByOtherApps whether or not this dex file was not loaded by its owning package
* @param primaryOrSplit whether or not the dex file is a primary/split dex. True indicates
* the file is either primary or a split. False indicates the file is secondary dex.
+ * @param loadingPackageName the package performing the load. Recorded only if it is different
+ * than {@param owningPackageName}.
* @return true if the dex load constitutes new information, or false if this information
* has been seen before.
*/
public boolean record(String owningPackageName, String dexPath, int ownerUserId,
- String loaderIsa, boolean isUsedByOtherApps, boolean primaryOrSplit) {
+ String loaderIsa, boolean isUsedByOtherApps, boolean primaryOrSplit,
+ String loadingPackageName, String classLoaderContext) {
if (!PackageManagerServiceUtils.checkISA(loaderIsa)) {
throw new IllegalArgumentException("loaderIsa " + loaderIsa + " is unsupported");
}
+ if (classLoaderContext == null) {
+ throw new IllegalArgumentException("Null classLoaderContext");
+ }
+
synchronized (mPackageUseInfoMap) {
PackageUseInfo packageUseInfo = mPackageUseInfoMap.get(owningPackageName);
if (packageUseInfo == null) {
@@ -99,12 +135,16 @@
// If we have a primary or a split apk, set isUsedByOtherApps.
// We do not need to record the loaderIsa or the owner because we compile
// primaries for all users and all ISAs.
- packageUseInfo.mIsUsedByOtherApps = isUsedByOtherApps;
+ packageUseInfo.mergeCodePathUsedByOtherApps(dexPath, isUsedByOtherApps,
+ owningPackageName, loadingPackageName);
} else {
// For secondary dex files record the loaderISA and the owner. We'll need
// to know under which user to compile and for what ISA.
- packageUseInfo.mDexUseInfoMap.put(
- dexPath, new DexUseInfo(isUsedByOtherApps, ownerUserId, loaderIsa));
+ DexUseInfo newData = new DexUseInfo(isUsedByOtherApps, ownerUserId,
+ classLoaderContext, loaderIsa);
+ packageUseInfo.mDexUseInfoMap.put(dexPath, newData);
+ maybeAddLoadingPackage(owningPackageName, loadingPackageName,
+ newData.mLoadingPackages);
}
mPackageUseInfoMap.put(owningPackageName, packageUseInfo);
return true;
@@ -113,10 +153,14 @@
if (primaryOrSplit) {
// We have a possible update on the primary apk usage. Merge
// isUsedByOtherApps information and return if there was an update.
- return packageUseInfo.merge(isUsedByOtherApps);
+ return packageUseInfo.mergeCodePathUsedByOtherApps(
+ dexPath, isUsedByOtherApps, owningPackageName, loadingPackageName);
} else {
DexUseInfo newData = new DexUseInfo(
- isUsedByOtherApps, ownerUserId, loaderIsa);
+ isUsedByOtherApps, ownerUserId, classLoaderContext, loaderIsa);
+ boolean updateLoadingPackages = maybeAddLoadingPackage(owningPackageName,
+ loadingPackageName, newData.mLoadingPackages);
+
DexUseInfo existingData = packageUseInfo.mDexUseInfoMap.get(dexPath);
if (existingData == null) {
// It's the first time we see this dex file.
@@ -138,7 +182,7 @@
}
// Merge the information into the existing data.
// Returns true if there was an update.
- return existingData.merge(newData);
+ return existingData.merge(newData) || updateLoadingPackages;
}
}
}
@@ -157,8 +201,12 @@
* Convenience method for async writes which does not force the user to pass a useless
* (Void) null.
*/
- public void maybeWriteAsync() {
- maybeWriteAsync((Void) null);
+ /*package*/ void maybeWriteAsync() {
+ maybeWriteAsync(null);
+ }
+
+ /*package*/ void writeNow() {
+ writeInternal(null);
}
@Override
@@ -185,16 +233,18 @@
*
* file_magic_version
* package_name_1
+ * +code_path1
+ * @ loading_package_1_1, loading_package_1_2...
+ * +code_path2
+ * @ loading_package_2_1, loading_package_2_2...
* #dex_file_path_1_1
* user_1_1, used_by_other_app_1_1, user_isa_1_1_1, user_isa_1_1_2
+ * @ loading_package_1_1_1, loading_package_1_1_2...
+ * class_loader_context_1_1
* #dex_file_path_1_2
* user_1_2, used_by_other_app_1_2, user_isa_1_2_1, user_isa_1_2_2
- * ...
- * package_name_2
- * #dex_file_path_2_1
- * user_2_1, used_by_other_app_2_1, user_isa_2_1_1, user_isa_2_1_2
- * #dex_file_path_2_2,
- * user_2_2, used_by_other_app_2_2, user_isa_2_2_1, user_isa_2_2_2
+ * @ loading_package_1_2_1, loading_package_1_2_2...
+ * class_loader_context_1_2
* ...
*/
/* package */ void write(Writer out) {
@@ -211,9 +261,16 @@
// Write the package line.
String packageName = pEntry.getKey();
PackageUseInfo packageUseInfo = pEntry.getValue();
+ fpw.println(packageName);
- fpw.println(String.join(SPLIT_CHAR, packageName,
- writeBoolean(packageUseInfo.mIsUsedByOtherApps)));
+ // Write the code paths used by other apps.
+ for (Map.Entry<String, Set<String>> codeEntry :
+ packageUseInfo.mCodePathsUsedByOtherApps.entrySet()) {
+ String codePath = codeEntry.getKey();
+ Set<String> loadingPackages = codeEntry.getValue();
+ fpw.println(CODE_PATH_LINE_CHAR + codePath);
+ fpw.println(LOADING_PACKAGE_CHAR + String.join(SPLIT_CHAR, loadingPackages));
+ }
// Write dex file lines.
for (Map.Entry<String, DexUseInfo> dEntry : packageUseInfo.mDexUseInfoMap.entrySet()) {
@@ -221,11 +278,14 @@
DexUseInfo dexUseInfo = dEntry.getValue();
fpw.println(DEX_LINE_CHAR + dexPath);
fpw.print(String.join(SPLIT_CHAR, Integer.toString(dexUseInfo.mOwnerUserId),
- writeBoolean(dexUseInfo.mIsUsedByOtherApps)));
+ writeBoolean(dexUseInfo.mIsUsedByOtherApps)));
for (String isa : dexUseInfo.mLoaderIsas) {
fpw.print(SPLIT_CHAR + isa);
}
fpw.println();
+ fpw.println(LOADING_PACKAGE_CHAR
+ + String.join(SPLIT_CHAR, dexUseInfo.mLoadingPackages));
+ fpw.println(dexUseInfo.getClassLoaderContext());
}
}
fpw.flush();
@@ -252,6 +312,7 @@
BufferedReader in = new BufferedReader(reader);
// Read header, do version check.
String versionLine = in.readLine();
+ int version;
if (versionLine == null) {
throw new IllegalStateException("No version line found.");
} else {
@@ -259,48 +320,56 @@
// TODO(calin): the caller is responsible to clear the file.
throw new IllegalStateException("Invalid version line: " + versionLine);
}
- int version = Integer.parseInt(
+ version = Integer.parseInt(
versionLine.substring(PACKAGE_DEX_USAGE_VERSION_HEADER.length()));
- if (version != PACKAGE_DEX_USAGE_VERSION) {
+ if (!isSupportedVersion(version)) {
throw new IllegalStateException("Unexpected version: " + version);
}
}
- String s = null;
- String currentPakage = null;
- PackageUseInfo currentPakageData = null;
+ String line;
+ String currentPackage = null;
+ PackageUseInfo currentPackageData = null;
Set<String> supportedIsas = new HashSet<>();
for (String abi : Build.SUPPORTED_ABIS) {
supportedIsas.add(VMRuntime.getInstructionSet(abi));
}
- while ((s = in.readLine()) != null) {
- if (s.startsWith(DEX_LINE_CHAR)) {
+ while ((line = in.readLine()) != null) {
+ if (line.startsWith(DEX_LINE_CHAR)) {
// This is the start of the the dex lines.
- // We expect two lines for each dex entry:
+ // We expect 4 lines for each dex entry:
// #dexPaths
+ // @loading_package_1,loading_package_2,...
+ // class_loader_context
// onwerUserId,isUsedByOtherApps,isa1,isa2
- if (currentPakage == null) {
+ if (currentPackage == null) {
throw new IllegalStateException(
"Malformed PackageDexUsage file. Expected package line before dex line.");
}
- // First line is the dex path.
- String dexPath = s.substring(DEX_LINE_CHAR.length());
- // Next line is the dex data.
- s = in.readLine();
- if (s == null) {
- throw new IllegalStateException("Could not fine dexUseInfo for line: " + s);
+ // Line 1 is the dex path.
+ String dexPath = line.substring(DEX_LINE_CHAR.length());
+
+ // Line 2 is the dex data: (userId, isUsedByOtherApps, isa).
+ line = in.readLine();
+ if (line == null) {
+ throw new IllegalStateException("Could not find dexUseInfo line");
+ }
+ String[] elems = line.split(SPLIT_CHAR);
+ if (elems.length < 3) {
+ throw new IllegalStateException("Invalid PackageDexUsage line: " + line);
}
- // We expect at least 3 elements (isUsedByOtherApps, userId, isa).
- String[] elems = s.split(SPLIT_CHAR);
- if (elems.length < 3) {
- throw new IllegalStateException("Invalid PackageDexUsage line: " + s);
- }
+ // In version 2 we added the loading packages and class loader context.
+ Set<String> loadingPackages = maybeReadLoadingPackages(in, version);
+ String classLoaderContext = maybeReadClassLoaderContext(in, version);
+
int ownerUserId = Integer.parseInt(elems[0]);
boolean isUsedByOtherApps = readBoolean(elems[1]);
- DexUseInfo dexUseInfo = new DexUseInfo(isUsedByOtherApps, ownerUserId);
+ DexUseInfo dexUseInfo = new DexUseInfo(isUsedByOtherApps, ownerUserId,
+ classLoaderContext, /*isa*/ null);
+ dexUseInfo.mLoadingPackages.addAll(loadingPackages);
for (int i = 2; i < elems.length; i++) {
String isa = elems[i];
if (supportedIsas.contains(isa)) {
@@ -317,18 +386,37 @@
"unsupported isas. dexPath=" + dexPath);
continue;
}
- currentPakageData.mDexUseInfoMap.put(dexPath, dexUseInfo);
+ currentPackageData.mDexUseInfoMap.put(dexPath, dexUseInfo);
+ } else if (line.startsWith(CODE_PATH_LINE_CHAR)) {
+ // This is a code path used by other apps line.
+ if (version < PACKAGE_DEX_USAGE_SUPPORTED_VERSION_2) {
+ throw new IllegalArgumentException("Unexpected code path line when parsing " +
+ "PackageDexUseData: " + line);
+ }
+
+ // Expects 2 lines:
+ // +code_paths
+ // @loading_packages
+ String codePath = line.substring(CODE_PATH_LINE_CHAR.length());
+ Set<String> loadingPackages = maybeReadLoadingPackages(in, version);
+ currentPackageData.mCodePathsUsedByOtherApps.put(codePath, loadingPackages);
} else {
// This is a package line.
- // We expect it to be: `packageName,isUsedByOtherApps`.
- String[] elems = s.split(SPLIT_CHAR);
- if (elems.length != 2) {
- throw new IllegalStateException("Invalid PackageDexUsage line: " + s);
+ if (version >= PACKAGE_DEX_USAGE_SUPPORTED_VERSION_2) {
+ currentPackage = line;
+ currentPackageData = new PackageUseInfo();
+ } else {
+ // Old version (<2)
+ // We expect it to be: `packageName,isUsedByOtherApps`.
+ String[] elems = line.split(SPLIT_CHAR);
+ if (elems.length != 2) {
+ throw new IllegalStateException("Invalid PackageDexUsage line: " + line);
+ }
+ currentPackage = elems[0];
+ currentPackageData = new PackageUseInfo();
+ currentPackageData.mUsedByOtherAppsBeforeUpgrade = readBoolean(elems[1]);
}
- currentPakage = elems[0];
- currentPakageData = new PackageUseInfo();
- currentPakageData.mIsUsedByOtherApps = readBoolean(elems[1]);
- data.put(currentPakage, currentPakageData);
+ data.put(currentPackage, currentPackageData);
}
}
@@ -339,9 +427,67 @@
}
/**
+ * Reads the class loader context encoding from the buffer {@code in} if
+ * {@code version} is at least {PACKAGE_DEX_USAGE_VERSION}.
+ */
+ private String maybeReadClassLoaderContext(BufferedReader in, int version) throws IOException {
+ String context = null;
+ if (version >= PACKAGE_DEX_USAGE_SUPPORTED_VERSION_2) {
+ context = in.readLine();
+ if (context == null) {
+ throw new IllegalStateException("Could not find the classLoaderContext line.");
+ }
+ }
+ // The context might be empty if we didn't have the chance to update it after a version
+ // upgrade. In this case return the special marker so that we recognize this is an unknown
+ // context.
+ return context == null ? UNKNOWN_CLASS_LOADER_CONTEXT : context;
+ }
+
+ /**
+ * Reads the list of loading packages from the buffer {@code in} if
+ * {@code version} is at least {PACKAGE_DEX_USAGE_SUPPORTED_VERSION_2}.
+ */
+ private Set<String> maybeReadLoadingPackages(BufferedReader in, int version)
+ throws IOException {
+ if (version >= PACKAGE_DEX_USAGE_SUPPORTED_VERSION_2) {
+ String line = in.readLine();
+ if (line == null) {
+ throw new IllegalStateException("Could not find the loadingPackages line.");
+ }
+ // We expect that most of the times the list of loading packages will be empty.
+ if (line.length() == LOADING_PACKAGE_CHAR.length()) {
+ return Collections.emptySet();
+ } else {
+ Set<String> result = new HashSet<>();
+ Collections.addAll(result,
+ line.substring(LOADING_PACKAGE_CHAR.length()).split(SPLIT_CHAR));
+ return result;
+ }
+ } else {
+ return Collections.emptySet();
+ }
+ }
+
+ /**
+ * Utility method which adds {@param loadingPackage} to {@param loadingPackages} only if it's
+ * not equal to {@param owningPackage}
+ */
+ private boolean maybeAddLoadingPackage(String owningPackage, String loadingPackage,
+ Set<String> loadingPackages) {
+ return !owningPackage.equals(loadingPackage) && loadingPackages.add(loadingPackage);
+ }
+
+ private boolean isSupportedVersion(int version) {
+ return version == PACKAGE_DEX_USAGE_SUPPORTED_VERSION_1
+ || version == PACKAGE_DEX_USAGE_SUPPORTED_VERSION_2;
+ }
+
+ /**
* Syncs the existing data with the set of available packages by removing obsolete entries.
*/
- public void syncData(Map<String, Set<Integer>> packageToUsersMap) {
+ /*package*/ void syncData(Map<String, Set<Integer>> packageToUsersMap,
+ Map<String, Set<String>> packageToCodePaths) {
synchronized (mPackageUseInfoMap) {
Iterator<Map.Entry<String, PackageUseInfo>> pIt =
mPackageUseInfoMap.entrySet().iterator();
@@ -365,8 +511,26 @@
dIt.remove();
}
}
- if (!packageUseInfo.mIsUsedByOtherApps
- && packageUseInfo.mDexUseInfoMap.isEmpty()) {
+
+ // Sync the code paths.
+ Set<String> codePaths = packageToCodePaths.get(packageName);
+ Iterator<Map.Entry<String, Set<String>>> codeIt =
+ packageUseInfo.mCodePathsUsedByOtherApps.entrySet().iterator();
+ while (codeIt.hasNext()) {
+ if (!codePaths.contains(codeIt.next().getKey())) {
+ codeIt.remove();
+ }
+ }
+
+ // In case the package was marked as used by other apps in a previous version
+ // propagate the flag to all the code paths.
+ // See mUsedByOtherAppsBeforeUpgrade docs on why it is important to do it.
+ if (packageUseInfo.mUsedByOtherAppsBeforeUpgrade) {
+ for (String codePath : codePaths) {
+ packageUseInfo.mergeCodePathUsedByOtherApps(codePath, true, null, null);
+ }
+ } else if (!packageUseInfo.isAnyCodePathUsedByOtherApps()
+ && packageUseInfo.mDexUseInfoMap.isEmpty()) {
// The package is not used by other apps and we removed all its dex files
// records. Remove the entire package record as well.
pIt.remove();
@@ -380,14 +544,13 @@
* Clears the {@code usesByOtherApps} marker for the package {@code packageName}.
* @return true if the package usage info was updated.
*/
- public boolean clearUsedByOtherApps(String packageName) {
+ /*package*/ boolean clearUsedByOtherApps(String packageName) {
synchronized (mPackageUseInfoMap) {
PackageUseInfo packageUseInfo = mPackageUseInfoMap.get(packageName);
- if (packageUseInfo == null || !packageUseInfo.mIsUsedByOtherApps) {
+ if (packageUseInfo == null) {
return false;
}
- packageUseInfo.mIsUsedByOtherApps = false;
- return true;
+ return packageUseInfo.clearCodePathUsedByOtherApps();
}
}
@@ -408,7 +571,7 @@
* @return true if the record was found and actually deleted,
* false if the record doesn't exist
*/
- public boolean removeUserPackage(String packageName, int userId) {
+ /*package*/ boolean removeUserPackage(String packageName, int userId) {
synchronized (mPackageUseInfoMap) {
PackageUseInfo packageUseInfo = mPackageUseInfoMap.get(packageName);
if (packageUseInfo == null) {
@@ -426,7 +589,8 @@
}
// If no secondary dex info is left and the package is not used by other apps
// remove the data since it is now useless.
- if (packageUseInfo.mDexUseInfoMap.isEmpty() && !packageUseInfo.mIsUsedByOtherApps) {
+ if (packageUseInfo.mDexUseInfoMap.isEmpty()
+ && !packageUseInfo.isAnyCodePathUsedByOtherApps()) {
mPackageUseInfoMap.remove(packageName);
updated = true;
}
@@ -440,7 +604,7 @@
* @return true if the record was found and actually deleted,
* false if the record doesn't exist
*/
- public boolean removeDexFile(String packageName, String dexFile, int userId) {
+ /*package*/ boolean removeDexFile(String packageName, String dexFile, int userId) {
synchronized (mPackageUseInfoMap) {
PackageUseInfo packageUseInfo = mPackageUseInfoMap.get(packageName);
if (packageUseInfo == null) {
@@ -462,7 +626,7 @@
return false;
}
- public PackageUseInfo getPackageUseInfo(String packageName) {
+ /*package*/ PackageUseInfo getPackageUseInfo(String packageName) {
synchronized (mPackageUseInfoMap) {
PackageUseInfo useInfo = mPackageUseInfoMap.get(packageName);
// The useInfo contains a map for secondary dex files which could be modified
@@ -477,7 +641,7 @@
/**
* Return all packages that contain records of secondary dex files.
*/
- public Set<String> getAllPackagesWithSecondaryDexFiles() {
+ /*package*/ Set<String> getAllPackagesWithSecondaryDexFiles() {
Set<String> packages = new HashSet<>();
synchronized (mPackageUseInfoMap) {
for (Map.Entry<String, PackageUseInfo> entry : mPackageUseInfoMap.entrySet()) {
@@ -515,15 +679,6 @@
throw new IllegalArgumentException("Unknown bool encoding: " + bool);
}
- private boolean contains(int[] array, int elem) {
- for (int i = 0; i < array.length; i++) {
- if (elem == array[i]) {
- return true;
- }
- }
- return false;
- }
-
public String dump() {
StringWriter sw = new StringWriter();
write(sw);
@@ -534,39 +689,95 @@
* Stores data on how a package and its dex files are used.
*/
public static class PackageUseInfo {
- // This flag is for the primary and split apks. It is set to true whenever one of them
- // is loaded by another app.
- private boolean mIsUsedByOtherApps;
+ // The app's code paths that are used by other apps.
+ // The key is the code path and the value is the set of loading packages.
+ private final Map<String, Set<String>> mCodePathsUsedByOtherApps;
// Map dex paths to their data (isUsedByOtherApps, owner id, loader isa).
private final Map<String, DexUseInfo> mDexUseInfoMap;
+ // Keeps track of whether or not this package was used by other apps before
+ // we upgraded to VERSION 4 which records the info for each code path separately.
+ // This is unwanted complexity but without it we risk to profile guide compile
+ // something that supposed to be shared. For example:
+ // 1) we determine that chrome is used by another app
+ // 2) we take an OTA which upgrades the way we keep track of usage data
+ // 3) chrome doesn't get used until the background job executes
+ // 4) as part of the backgound job we now think that chrome is not used by others
+ // and we speed-profile.
+ // 5) as a result the next time someone uses chrome it will extract from apk since
+ // the compiled code will be private.
+ private boolean mUsedByOtherAppsBeforeUpgrade;
+
public PackageUseInfo() {
- mIsUsedByOtherApps = false;
+ mCodePathsUsedByOtherApps = new HashMap<>();
mDexUseInfoMap = new HashMap<>();
}
// Creates a deep copy of the `other`.
public PackageUseInfo(PackageUseInfo other) {
- mIsUsedByOtherApps = other.mIsUsedByOtherApps;
+ mCodePathsUsedByOtherApps = new HashMap<>();
+ for (Map.Entry<String, Set<String>> e : other.mCodePathsUsedByOtherApps.entrySet()) {
+ mCodePathsUsedByOtherApps.put(e.getKey(), new HashSet<>(e.getValue()));
+ }
+
mDexUseInfoMap = new HashMap<>();
for (Map.Entry<String, DexUseInfo> e : other.mDexUseInfoMap.entrySet()) {
mDexUseInfoMap.put(e.getKey(), new DexUseInfo(e.getValue()));
}
}
- private boolean merge(boolean isUsedByOtherApps) {
- boolean oldIsUsedByOtherApps = mIsUsedByOtherApps;
- mIsUsedByOtherApps = mIsUsedByOtherApps || isUsedByOtherApps;
- return oldIsUsedByOtherApps != this.mIsUsedByOtherApps;
+ private boolean mergeCodePathUsedByOtherApps(String codePath, boolean isUsedByOtherApps,
+ String owningPackageName, String loadingPackage) {
+ if (!isUsedByOtherApps) {
+ // Nothing to update if the the code path is not used by other apps.
+ return false;
+ }
+
+ boolean newCodePath = false;
+ Set<String> loadingPackages = mCodePathsUsedByOtherApps.get(codePath);
+ if (loadingPackages == null) {
+ loadingPackages = new HashSet<>();
+ mCodePathsUsedByOtherApps.put(codePath, loadingPackages);
+ newCodePath = true;
+ }
+ boolean newLoadingPackage = loadingPackage != null
+ && !loadingPackage.equals(owningPackageName)
+ && loadingPackages.add(loadingPackage);
+ return newCodePath || newLoadingPackage;
}
- public boolean isUsedByOtherApps() {
- return mIsUsedByOtherApps;
+ public boolean isUsedByOtherApps(String codePath) {
+ return mCodePathsUsedByOtherApps.containsKey(codePath);
}
public Map<String, DexUseInfo> getDexUseInfoMap() {
return mDexUseInfoMap;
}
+
+ public Set<String> getLoadingPackages(String codePath) {
+ return mCodePathsUsedByOtherApps.getOrDefault(codePath, null);
+ }
+
+ public boolean isAnyCodePathUsedByOtherApps() {
+ return !mCodePathsUsedByOtherApps.isEmpty();
+ }
+
+ /**
+ * Clears the usedByOtherApps markers from all code paths.
+ * Returns whether or not there was an update.
+ */
+ /*package*/ boolean clearCodePathUsedByOtherApps() {
+ // Update mUsedByOtherAppsBeforeUpgrade as well to be consistent with
+ // the new data. This is not saved to disk so we don't need to return it.
+ mUsedByOtherAppsBeforeUpgrade = true;
+
+ if (mCodePathsUsedByOtherApps.isEmpty()) {
+ return false;
+ } else {
+ mCodePathsUsedByOtherApps.clear();
+ return true;
+ }
+ }
}
/**
@@ -575,33 +786,59 @@
public static class DexUseInfo {
private boolean mIsUsedByOtherApps;
private final int mOwnerUserId;
+ // The class loader context for the dex file. This encodes the class loader chain
+ // (class loader type + class path) in a format compatible to dex2oat.
+ // See {@code DexoptUtils.processContextForDexLoad}.
+ private String mClassLoaderContext;
+ // The instructions sets of the applications loading the dex file.
private final Set<String> mLoaderIsas;
+ // Packages who load this dex file.
+ private final Set<String> mLoadingPackages;
- public DexUseInfo(boolean isUsedByOtherApps, int ownerUserId) {
- this(isUsedByOtherApps, ownerUserId, null);
- }
-
- public DexUseInfo(boolean isUsedByOtherApps, int ownerUserId, String loaderIsa) {
+ public DexUseInfo(boolean isUsedByOtherApps, int ownerUserId, String classLoaderContext,
+ String loaderIsa) {
mIsUsedByOtherApps = isUsedByOtherApps;
mOwnerUserId = ownerUserId;
+ mClassLoaderContext = classLoaderContext;
mLoaderIsas = new HashSet<>();
if (loaderIsa != null) {
mLoaderIsas.add(loaderIsa);
}
+ mLoadingPackages = new HashSet<>();
}
// Creates a deep copy of the `other`.
public DexUseInfo(DexUseInfo other) {
mIsUsedByOtherApps = other.mIsUsedByOtherApps;
mOwnerUserId = other.mOwnerUserId;
+ mClassLoaderContext = other.mClassLoaderContext;
mLoaderIsas = new HashSet<>(other.mLoaderIsas);
+ mLoadingPackages = new HashSet<>(other.mLoadingPackages);
}
private boolean merge(DexUseInfo dexUseInfo) {
boolean oldIsUsedByOtherApps = mIsUsedByOtherApps;
mIsUsedByOtherApps = mIsUsedByOtherApps || dexUseInfo.mIsUsedByOtherApps;
boolean updateIsas = mLoaderIsas.addAll(dexUseInfo.mLoaderIsas);
- return updateIsas || (oldIsUsedByOtherApps != mIsUsedByOtherApps);
+ boolean updateLoadingPackages = mLoadingPackages.addAll(dexUseInfo.mLoadingPackages);
+
+ String oldClassLoaderContext = mClassLoaderContext;
+ if (UNKNOWN_CLASS_LOADER_CONTEXT.equals(mClassLoaderContext)) {
+ // Can happen if we read a previous version.
+ mClassLoaderContext = dexUseInfo.mClassLoaderContext;
+ } else if (UNSUPPORTED_CLASS_LOADER_CONTEXT.equals(dexUseInfo.mClassLoaderContext)) {
+ // We detected an unsupported context.
+ mClassLoaderContext = UNSUPPORTED_CLASS_LOADER_CONTEXT;
+ } else if (!UNSUPPORTED_CLASS_LOADER_CONTEXT.equals(mClassLoaderContext) &&
+ !Objects.equal(mClassLoaderContext, dexUseInfo.mClassLoaderContext)) {
+ // We detected a context change.
+ mClassLoaderContext = VARIABLE_CLASS_LOADER_CONTEXT;
+ }
+
+ return updateIsas ||
+ (oldIsUsedByOtherApps != mIsUsedByOtherApps) ||
+ updateLoadingPackages
+ || !Objects.equal(oldClassLoaderContext, mClassLoaderContext);
}
public boolean isUsedByOtherApps() {
@@ -615,5 +852,25 @@
public Set<String> getLoaderIsas() {
return mLoaderIsas;
}
+
+ public Set<String> getLoadingPackages() {
+ return mLoadingPackages;
+ }
+
+ public String getClassLoaderContext() { return mClassLoaderContext; }
+
+ public boolean isUnsupportedClassLoaderContext() {
+ return UNSUPPORTED_CLASS_LOADER_CONTEXT.equals(mClassLoaderContext);
+ }
+
+ 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);
+ }
+
+ public boolean isVariableClassLoaderContext() {
+ return VARIABLE_CLASS_LOADER_CONTEXT.equals(mClassLoaderContext);
+ }
}
}
diff --git a/services/net/java/android/net/apf/ApfFilter.java b/services/net/java/android/net/apf/ApfFilter.java
index 5c2b66f..31a1abb 100644
--- a/services/net/java/android/net/apf/ApfFilter.java
+++ b/services/net/java/android/net/apf/ApfFilter.java
@@ -86,6 +86,14 @@
*/
public class ApfFilter {
+ // Helper class for specifying functional filter parameters.
+ public static class ApfConfiguration {
+ public ApfCapabilities apfCapabilities;
+ public boolean multicastFilter;
+ public boolean ieee802_3Filter;
+ public int[] ethTypeBlackList;
+ }
+
// Enums describing the outcome of receiving an RA packet.
private static enum ProcessRaResult {
MATCH, // Received RA matched a known RA
@@ -261,17 +269,16 @@
private int mIPv4PrefixLength;
@VisibleForTesting
- ApfFilter(ApfCapabilities apfCapabilities, NetworkInterface networkInterface,
- IpClient.Callback ipClientCallback, boolean multicastFilter,
- boolean ieee802_3Filter, int[] ethTypeBlackList, IpConnectivityLog log) {
- mApfCapabilities = apfCapabilities;
+ ApfFilter(ApfConfiguration config, NetworkInterface networkInterface,
+ IpClient.Callback ipClientCallback, IpConnectivityLog log) {
+ mApfCapabilities = config.apfCapabilities;
mIpClientCallback = ipClientCallback;
mNetworkInterface = networkInterface;
- mMulticastFilter = multicastFilter;
- mDrop802_3Frames = ieee802_3Filter;
+ mMulticastFilter = config.multicastFilter;
+ mDrop802_3Frames = config.ieee802_3Filter;
// Now fill the black list from the passed array
- mEthTypeBlackList = filterEthTypeBlackList(ethTypeBlackList);
+ mEthTypeBlackList = filterEthTypeBlackList(config.ethTypeBlackList);
mMetricsLog = log;
@@ -1160,9 +1167,10 @@
* Create an {@link ApfFilter} if {@code apfCapabilities} indicates support for packet
* filtering using APF programs.
*/
- public static ApfFilter maybeCreate(ApfCapabilities apfCapabilities,
- NetworkInterface networkInterface, IpClient.Callback ipClientCallback,
- boolean multicastFilter, boolean ieee802_3Filter, int[] ethTypeBlackList) {
+ public static ApfFilter maybeCreate(ApfConfiguration config,
+ NetworkInterface networkInterface, IpClient.Callback ipClientCallback) {
+ if (config == null) return null;
+ ApfCapabilities apfCapabilities = config.apfCapabilities;
if (apfCapabilities == null || networkInterface == null) return null;
if (apfCapabilities.apfVersionSupported == 0) return null;
if (apfCapabilities.maximumApfProgramSize < 512) {
@@ -1178,8 +1186,7 @@
Log.e(TAG, "Unsupported APF version: " + apfCapabilities.apfVersionSupported);
return null;
}
- return new ApfFilter(apfCapabilities, networkInterface, ipClientCallback,
- multicastFilter, ieee802_3Filter, ethTypeBlackList, new IpConnectivityLog());
+ return new ApfFilter(config, networkInterface, ipClientCallback, new IpConnectivityLog());
}
public synchronized void shutdown() {
diff --git a/services/net/java/android/net/ip/IpClient.java b/services/net/java/android/net/ip/IpClient.java
index 2359fab..70983c8 100644
--- a/services/net/java/android/net/ip/IpClient.java
+++ b/services/net/java/android/net/ip/IpClient.java
@@ -310,12 +310,12 @@
return this;
}
- public Builder withIPv6AddrGenModeEUI64() {
+ public Builder withRandomMacAddress() {
mConfig.mIPv6AddrGenMode = INetd.IPV6_ADDR_GEN_MODE_EUI64;
return this;
}
- public Builder withIPv6AddrGenModeStablePrivacy() {
+ public Builder withStableMacAddress() {
mConfig.mIPv6AddrGenMode = INetd.IPV6_ADDR_GEN_MODE_STABLE_PRIVACY;
return this;
}
@@ -1429,15 +1429,15 @@
@Override
public void enter() {
+ ApfFilter.ApfConfiguration apfConfig = new ApfFilter.ApfConfiguration();
+ apfConfig.apfCapabilities = mConfiguration.mApfCapabilities;
+ apfConfig.multicastFilter = mMulticastFiltering;
// Get the Configuration for ApfFilter from Context
- final boolean filter802_3Frames =
+ apfConfig.ieee802_3Filter =
mContext.getResources().getBoolean(R.bool.config_apfDrop802_3Frames);
-
- final int[] ethTypeBlackList = mContext.getResources().getIntArray(
- R.array.config_apfEthTypeBlackList);
-
- mApfFilter = ApfFilter.maybeCreate(mConfiguration.mApfCapabilities, mNetworkInterface,
- mCallback, mMulticastFiltering, filter802_3Frames, ethTypeBlackList);
+ apfConfig.ethTypeBlackList =
+ mContext.getResources().getIntArray(R.array.config_apfEthTypeBlackList);
+ mApfFilter = ApfFilter.maybeCreate(apfConfig, mNetworkInterface, mCallback);
// TODO: investigate the effects of any multicast filtering racing/interfering with the
// rest of this IP configuration startup.
if (mApfFilter == null) {
diff --git a/services/net/java/android/net/ip/IpManager.java b/services/net/java/android/net/ip/IpManager.java
index b12cb32..3898145 100644
--- a/services/net/java/android/net/ip/IpManager.java
+++ b/services/net/java/android/net/ip/IpManager.java
@@ -88,16 +88,6 @@
return this;
}
@Override
- public Builder withIPv6AddrGenModeEUI64() {
- super.withIPv6AddrGenModeEUI64();
- return this;
- }
- @Override
- public Builder withIPv6AddrGenModeStablePrivacy() {
- super.withIPv6AddrGenModeStablePrivacy();
- return this;
- }
- @Override
public Builder withNetwork(Network network) {
super.withNetwork(network);
return this;
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 afc0f67..4db9a30 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
@@ -23,10 +23,14 @@
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
+import dalvik.system.DelegateLastClassLoader;
+import dalvik.system.PathClassLoader;
import dalvik.system.VMRuntime;
+import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -48,6 +52,10 @@
@RunWith(AndroidJUnit4.class)
@SmallTest
public class DexManagerTests {
+ private static final String PATH_CLASS_LOADER_NAME = PathClassLoader.class.getName();
+ private static final String DELEGATE_LAST_CLASS_LOADER_NAME =
+ DelegateLastClassLoader.class.getName();
+
private DexManager mDexManager;
private TestData mFooUser0;
@@ -56,6 +64,9 @@
private TestData mInvalidIsa;
private TestData mDoesNotExist;
+ private TestData mBarUser0UnsupportedClassLoader;
+ private TestData mBarUser0DelegateLastClassLoader;
+
private int mUser0;
private int mUser1;
@@ -68,12 +79,17 @@
String foo = "foo";
String bar = "bar";
- mFooUser0 = new TestData(foo, isa, mUser0);
- mBarUser0 = new TestData(bar, isa, mUser0);
- mBarUser1 = new TestData(bar, isa, mUser1);
+ mFooUser0 = new TestData(foo, isa, mUser0, PATH_CLASS_LOADER_NAME);
+ mBarUser0 = new TestData(bar, isa, mUser0, PATH_CLASS_LOADER_NAME);
+ mBarUser1 = new TestData(bar, isa, mUser1, PATH_CLASS_LOADER_NAME);
mInvalidIsa = new TestData("INVALID", "INVALID_ISA", mUser0);
mDoesNotExist = new TestData("DOES.NOT.EXIST", isa, mUser1);
+ mBarUser0UnsupportedClassLoader = new TestData(bar, isa, mUser0,
+ "unsupported.class_loader");
+ mBarUser0DelegateLastClassLoader = new TestData(bar, isa, mUser0,
+ DELEGATE_LAST_CLASS_LOADER_NAME);
+
mDexManager = new DexManager(null, null, null, null);
// Foo and Bar are available to user0.
@@ -90,7 +106,7 @@
notifyDexLoad(mFooUser0, mFooUser0.getBaseAndSplitDexPaths(), mUser0);
// Package is not used by others, so we should get nothing back.
- assertNull(getPackageUseInfo(mFooUser0));
+ assertNoUseInfo(mFooUser0);
}
@Test
@@ -100,8 +116,7 @@
// Bar is used by others now and should be in our records
PackageUseInfo pui = getPackageUseInfo(mBarUser0);
- assertNotNull(pui);
- assertTrue(pui.isUsedByOtherApps());
+ assertIsUsedByOtherApps(mBarUser0, pui, true);
assertTrue(pui.getDexUseInfoMap().isEmpty());
}
@@ -112,8 +127,7 @@
notifyDexLoad(mFooUser0, fooSecondaries, mUser0);
PackageUseInfo pui = getPackageUseInfo(mFooUser0);
- assertNotNull(pui);
- assertFalse(pui.isUsedByOtherApps());
+ assertIsUsedByOtherApps(mFooUser0, pui, false);
assertEquals(fooSecondaries.size(), pui.getDexUseInfoMap().size());
assertSecondaryUse(mFooUser0, pui, fooSecondaries, /*isUsedByOtherApps*/false, mUser0);
}
@@ -125,8 +139,7 @@
notifyDexLoad(mFooUser0, barSecondaries, mUser0);
PackageUseInfo pui = getPackageUseInfo(mBarUser0);
- assertNotNull(pui);
- assertFalse(pui.isUsedByOtherApps());
+ assertIsUsedByOtherApps(mBarUser0, pui, false);
assertEquals(barSecondaries.size(), pui.getDexUseInfoMap().size());
assertSecondaryUse(mFooUser0, pui, barSecondaries, /*isUsedByOtherApps*/true, mUser0);
}
@@ -149,8 +162,7 @@
// Check bar usage. Should be used by other app (for primary and barSecondaries).
PackageUseInfo pui = getPackageUseInfo(mBarUser0);
- assertNotNull(pui);
- assertTrue(pui.isUsedByOtherApps());
+ assertIsUsedByOtherApps(mBarUser0, pui, true);
assertEquals(barSecondaries.size() + barSecondariesForOwnUse.size(),
pui.getDexUseInfoMap().size());
@@ -160,8 +172,7 @@
// Check foo usage. Should not be used by other app.
pui = getPackageUseInfo(mFooUser0);
- assertNotNull(pui);
- assertFalse(pui.isUsedByOtherApps());
+ assertIsUsedByOtherApps(mFooUser0, pui, false);
assertEquals(fooSecondaries.size(), pui.getDexUseInfoMap().size());
assertSecondaryUse(mFooUser0, pui, fooSecondaries, /*isUsedByOtherApps*/false, mUser0);
}
@@ -169,22 +180,22 @@
@Test
public void testPackageUseInfoNotFound() {
// Assert we don't get back data we did not previously record.
- assertNull(getPackageUseInfo(mFooUser0));
+ assertNoUseInfo(mFooUser0);
}
@Test
public void testInvalidIsa() {
// Notifying with an invalid ISA should be ignored.
notifyDexLoad(mInvalidIsa, mInvalidIsa.getSecondaryDexPaths(), mUser0);
- assertNull(getPackageUseInfo(mInvalidIsa));
+ assertNoUseInfo(mInvalidIsa);
}
@Test
- public void testNotExistingPackate() {
+ public void testNotExistingPackage() {
// Notifying about the load of a package which was previously not
// register in DexManager#load should be ignored.
notifyDexLoad(mDoesNotExist, mDoesNotExist.getBaseAndSplitDexPaths(), mUser0);
- assertNull(getPackageUseInfo(mDoesNotExist));
+ assertNoUseInfo(mDoesNotExist);
}
@Test
@@ -192,7 +203,7 @@
// Bar from User1 tries to load secondary dex files from User0 Bar.
// Request should be ignored.
notifyDexLoad(mBarUser1, mBarUser0.getSecondaryDexPaths(), mUser1);
- assertNull(getPackageUseInfo(mBarUser1));
+ assertNoUseInfo(mBarUser1);
}
@Test
@@ -201,7 +212,7 @@
// Note that the PackageManagerService already filters this out but we
// still check that nothing goes unexpected in DexManager.
notifyDexLoad(mBarUser0, mFooUser0.getBaseAndSplitDexPaths(), mUser1);
- assertNull(getPackageUseInfo(mBarUser1));
+ assertNoUseInfo(mBarUser1);
}
@Test
@@ -213,7 +224,7 @@
// Before we notify about the installation of the newPackage if mFoo
// is trying to load something from it we should not find it.
notifyDexLoad(mFooUser0, newSecondaries, mUser0);
- assertNull(getPackageUseInfo(newPackage));
+ assertNoUseInfo(newPackage);
// Notify about newPackage install and let mFoo load its dexes.
mDexManager.notifyPackageInstalled(newPackage.mPackageInfo, mUser0);
@@ -221,8 +232,7 @@
// We should get back the right info.
PackageUseInfo pui = getPackageUseInfo(newPackage);
- assertNotNull(pui);
- assertFalse(pui.isUsedByOtherApps());
+ assertIsUsedByOtherApps(newPackage, pui, false);
assertEquals(newSecondaries.size(), pui.getDexUseInfoMap().size());
assertSecondaryUse(newPackage, pui, newSecondaries, /*isUsedByOtherApps*/true, mUser0);
}
@@ -238,8 +248,7 @@
notifyDexLoad(newPackage, newSecondaries, mUser0);
PackageUseInfo pui = getPackageUseInfo(newPackage);
- assertNotNull(pui);
- assertFalse(pui.isUsedByOtherApps());
+ assertIsUsedByOtherApps(newPackage, pui, false);
assertEquals(newSecondaries.size(), pui.getDexUseInfoMap().size());
assertSecondaryUse(newPackage, pui, newSecondaries, /*isUsedByOtherApps*/false, mUser0);
}
@@ -251,8 +260,7 @@
// Bar is used by others now and should be in our records.
PackageUseInfo pui = getPackageUseInfo(mBarUser0);
- assertNotNull(pui);
- assertTrue(pui.isUsedByOtherApps());
+ assertIsUsedByOtherApps(mBarUser0, pui, true);
assertTrue(pui.getDexUseInfoMap().isEmpty());
// Notify that bar is updated.
@@ -262,8 +270,7 @@
// The usedByOtherApps flag should be clear now.
pui = getPackageUseInfo(mBarUser0);
- assertNotNull(pui);
- assertFalse(pui.isUsedByOtherApps());
+ assertIsUsedByOtherApps(mBarUser0, pui, false);
}
@Test
@@ -275,8 +282,7 @@
// We shouldn't find yet the new split as we didn't notify the package update.
notifyDexLoad(mFooUser0, newSplits, mUser0);
- PackageUseInfo pui = getPackageUseInfo(mBarUser0);
- assertNull(pui);
+ assertNoUseInfo(mBarUser0);
// Notify that bar is updated. splitSourceDirs will contain the updated path.
mDexManager.notifyPackageUpdated(mBarUser0.getPackageName(),
@@ -285,9 +291,9 @@
// Now, when the split is loaded we will find it and we should mark Bar as usedByOthers.
notifyDexLoad(mFooUser0, newSplits, mUser0);
- pui = getPackageUseInfo(mBarUser0);
+ PackageUseInfo pui = getPackageUseInfo(mBarUser0);
assertNotNull(pui);
- assertTrue(pui.isUsedByOtherApps());
+ assertIsUsedByOtherApps(newSplits, pui, true);
}
@Test
@@ -319,8 +325,7 @@
// Foo should still be around since it's used by other apps but with no
// secondary dex info.
PackageUseInfo pui = getPackageUseInfo(mFooUser0);
- assertNotNull(pui);
- assertTrue(pui.isUsedByOtherApps());
+ assertIsUsedByOtherApps(mFooUser0, pui, true);
assertTrue(pui.getDexUseInfoMap().isEmpty());
}
@@ -334,8 +339,7 @@
// Foo should not be around since all its secondary dex info were deleted
// and it is not used by other apps.
- PackageUseInfo pui = getPackageUseInfo(mFooUser0);
- assertNull(pui);
+ assertNoUseInfo(mFooUser0);
}
@Test
@@ -347,8 +351,7 @@
mDexManager.notifyPackageDataDestroyed(mBarUser0.getPackageName(), UserHandle.USER_ALL);
// Bar should not be around since it was removed for all users.
- PackageUseInfo pui = getPackageUseInfo(mBarUser0);
- assertNull(pui);
+ assertNoUseInfo(mBarUser0);
}
@Test
@@ -357,7 +360,7 @@
// Load a dex file from framework.
notifyDexLoad(mFooUser0, Arrays.asList(frameworkDex), mUser0);
// The dex file should not be recognized as a package.
- assertNull(mDexManager.getPackageUseInfo(frameworkDex));
+ assertFalse(mDexManager.hasInfoOnPackage(frameworkDex));
}
@Test
@@ -367,14 +370,83 @@
notifyDexLoad(mFooUser0, fooSecondaries, mUser0);
PackageUseInfo pui = getPackageUseInfo(mFooUser0);
- assertNotNull(pui);
- assertFalse(pui.isUsedByOtherApps());
+ assertIsUsedByOtherApps(mFooUser0, pui, false);
assertEquals(fooSecondaries.size(), pui.getDexUseInfoMap().size());
assertSecondaryUse(mFooUser0, pui, fooSecondaries, /*isUsedByOtherApps*/false, mUser0);
}
+ @Test
+ public void testNotifyUnsupportedClassLoader() {
+ List<String> secondaries = mBarUser0UnsupportedClassLoader.getSecondaryDexPaths();
+ notifyDexLoad(mBarUser0UnsupportedClassLoader, secondaries, mUser0);
+
+ PackageUseInfo pui = getPackageUseInfo(mBarUser0UnsupportedClassLoader);
+ assertIsUsedByOtherApps(mBarUser0UnsupportedClassLoader, pui, false);
+ assertEquals(secondaries.size(), pui.getDexUseInfoMap().size());
+ // We expect that all the contexts are unsupported.
+ String[] expectedContexts =
+ Collections.nCopies(secondaries.size(),
+ PackageDexUsage.UNSUPPORTED_CLASS_LOADER_CONTEXT).toArray(new String[0]);
+ assertSecondaryUse(mBarUser0UnsupportedClassLoader, pui, secondaries,
+ /*isUsedByOtherApps*/false, mUser0, expectedContexts);
+ }
+
+ @Test
+ public void testNotifyVariableClassLoader() {
+ // Record bar secondaries with the default PathClassLoader.
+ List<String> secondaries = mBarUser0.getSecondaryDexPaths();
+
+ notifyDexLoad(mBarUser0, secondaries, mUser0);
+ PackageUseInfo pui = getPackageUseInfo(mBarUser0);
+ assertIsUsedByOtherApps(mBarUser0, pui, false);
+ assertEquals(secondaries.size(), pui.getDexUseInfoMap().size());
+ assertSecondaryUse(mFooUser0, pui, secondaries, /*isUsedByOtherApps*/false, mUser0);
+
+ // Record bar secondaries again with a different class loader. This will change the context.
+ notifyDexLoad(mBarUser0DelegateLastClassLoader, secondaries, mUser0);
+
+ pui = getPackageUseInfo(mBarUser0);
+ assertIsUsedByOtherApps(mBarUser0, pui, false);
+ assertEquals(secondaries.size(), pui.getDexUseInfoMap().size());
+ // We expect that all the contexts to be changed to variable now.
+ String[] expectedContexts =
+ Collections.nCopies(secondaries.size(),
+ PackageDexUsage.VARIABLE_CLASS_LOADER_CONTEXT).toArray(new String[0]);
+ assertSecondaryUse(mFooUser0, pui, secondaries, /*isUsedByOtherApps*/false, mUser0,
+ expectedContexts);
+ }
+
+ @Test
+ public void testNotifyUnsupportedClassLoaderDoesNotChange() {
+ List<String> secondaries = mBarUser0UnsupportedClassLoader.getSecondaryDexPaths();
+ notifyDexLoad(mBarUser0UnsupportedClassLoader, secondaries, mUser0);
+
+ PackageUseInfo pui = getPackageUseInfo(mBarUser0UnsupportedClassLoader);
+ assertIsUsedByOtherApps(mBarUser0UnsupportedClassLoader, pui, false);
+ assertEquals(secondaries.size(), pui.getDexUseInfoMap().size());
+ // We expect that all the contexts are unsupported.
+ String[] expectedContexts =
+ Collections.nCopies(secondaries.size(),
+ PackageDexUsage.UNSUPPORTED_CLASS_LOADER_CONTEXT).toArray(new String[0]);
+ assertSecondaryUse(mBarUser0UnsupportedClassLoader, pui, secondaries,
+ /*isUsedByOtherApps*/false, mUser0, expectedContexts);
+
+ // Record bar secondaries again with a different class loader. This will change the context.
+ // However, because the context was already marked as unsupported we should not chage it.
+ notifyDexLoad(mBarUser0DelegateLastClassLoader, secondaries, mUser0);
+ pui = getPackageUseInfo(mBarUser0UnsupportedClassLoader);
+ assertSecondaryUse(mBarUser0UnsupportedClassLoader, pui, secondaries,
+ /*isUsedByOtherApps*/false, mUser0, expectedContexts);
+
+ }
+
+
private void assertSecondaryUse(TestData testData, PackageUseInfo pui,
- List<String> secondaries, boolean isUsedByOtherApps, int ownerUserId) {
+ List<String> secondaries, boolean isUsedByOtherApps, int ownerUserId,
+ String[] expectedContexts) {
+ assertNotNull(expectedContexts);
+ assertEquals(expectedContexts.length, secondaries.size());
+ int index = 0;
for (String dex : secondaries) {
DexUseInfo dui = pui.getDexUseInfoMap().get(dex);
assertNotNull(dui);
@@ -382,16 +454,50 @@
assertEquals(ownerUserId, dui.getOwnerUserId());
assertEquals(1, dui.getLoaderIsas().size());
assertTrue(dui.getLoaderIsas().contains(testData.mLoaderIsa));
+ assertEquals(expectedContexts[index++], dui.getClassLoaderContext());
}
}
+ private void assertSecondaryUse(TestData testData, PackageUseInfo pui,
+ List<String> secondaries, boolean isUsedByOtherApps, int ownerUserId) {
+ String[] expectedContexts = DexoptUtils.processContextForDexLoad(
+ Arrays.asList(testData.mClassLoader),
+ Arrays.asList(String.join(File.pathSeparator, secondaries)));
+ assertSecondaryUse(testData, pui, secondaries, isUsedByOtherApps, ownerUserId,
+ expectedContexts);
+ }
+ private void assertIsUsedByOtherApps(TestData testData, PackageUseInfo pui,
+ boolean isUsedByOtherApps) {
+ assertIsUsedByOtherApps(testData.getBaseAndSplitDexPaths(), pui, isUsedByOtherApps);
+ }
+
+ private void assertIsUsedByOtherApps(List<String> codePaths, PackageUseInfo pui,
+ boolean isUsedByOtherApps) {
+ for (String codePath : codePaths) {
+ assertEquals(codePath, isUsedByOtherApps, pui.isUsedByOtherApps(codePath));
+ }
+ }
private void notifyDexLoad(TestData testData, List<String> dexPaths, int loaderUserId) {
- mDexManager.notifyDexLoad(testData.mPackageInfo.applicationInfo, dexPaths,
+ // 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 = Arrays.asList(String.join(File.pathSeparator, dexPaths));
+ notifyDexLoad(testData, classLoaders, classPaths, loaderUserId);
+ }
+
+ private void notifyDexLoad(TestData testData, List<String> classLoader, List<String> classPaths,
+ int loaderUserId) {
+ mDexManager.notifyDexLoad(testData.mPackageInfo.applicationInfo, classLoader, classPaths,
testData.mLoaderIsa, loaderUserId);
}
private PackageUseInfo getPackageUseInfo(TestData testData) {
- return mDexManager.getPackageUseInfo(testData.mPackageInfo.packageName);
+ assertTrue(mDexManager.hasInfoOnPackage(testData.mPackageInfo.packageName));
+ return mDexManager.getPackageUseInfoOrDefault(testData.mPackageInfo.packageName);
+ }
+
+ private void assertNoUseInfo(TestData testData) {
+ assertFalse(mDexManager.hasInfoOnPackage(testData.mPackageInfo.packageName));
}
private static PackageInfo getMockPackageInfo(String packageName, int userId) {
@@ -416,10 +522,16 @@
private static class TestData {
private final PackageInfo mPackageInfo;
private final String mLoaderIsa;
+ private final String mClassLoader;
- private TestData(String packageName, String loaderIsa, int userId) {
+ private TestData(String packageName, String loaderIsa, int userId, String classLoader) {
mPackageInfo = getMockPackageInfo(packageName, userId);
mLoaderIsa = loaderIsa;
+ mClassLoader = classLoader;
+ }
+
+ private TestData(String packageName, String loaderIsa, int userId) {
+ this(packageName, loaderIsa, userId, PATH_CLASS_LOADER_NAME);
}
private String getPackageName() {
diff --git a/services/tests/servicestests/src/com/android/server/pm/dex/DexoptOptionsTests.java b/services/tests/servicestests/src/com/android/server/pm/dex/DexoptOptionsTests.java
new file mode 100644
index 0000000..c2072df
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/pm/dex/DexoptOptionsTests.java
@@ -0,0 +1,143 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.pm.dex;
+
+
+import static com.android.server.pm.PackageManagerServiceCompilerMapping.getCompilerFilterForReason;
+
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import com.android.server.pm.PackageManagerService;
+import com.android.server.pm.PackageManagerServiceCompilerMapping;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class DexoptOptionsTests {
+ private final static String mPackageName = "test.android.com";
+ private final static String mCompilerFilter =
+ PackageManagerServiceCompilerMapping.getDefaultCompilerFilter();
+ private final static String mSplitName = "split-A.apk";
+
+ @Test
+ public void testCreateDexoptOptionsEmpty() {
+ DexoptOptions opt = new DexoptOptions(mPackageName, mCompilerFilter, /*flags*/ 0);
+ assertEquals(mPackageName, opt.getPackageName());
+ assertEquals(mCompilerFilter, opt.getCompilerFilter());
+ assertEquals(null, opt.getSplitName());
+ assertFalse(opt.isBootComplete());
+ assertFalse(opt.isCheckForProfileUpdates());
+ assertFalse(opt.isDexoptOnlySecondaryDex());
+ assertFalse(opt.isDexoptOnlySharedDex());
+ assertFalse(opt.isDowngrade());
+ assertFalse(opt.isForce());
+ assertFalse(opt.isDexoptIdleBackgroundJob());
+ }
+
+ @Test
+ public void testCreateDexoptOptionsFull() {
+ int flags =
+ DexoptOptions.DEXOPT_FORCE |
+ DexoptOptions.DEXOPT_BOOT_COMPLETE |
+ DexoptOptions.DEXOPT_CHECK_FOR_PROFILES_UPDATES |
+ DexoptOptions.DEXOPT_ONLY_SECONDARY_DEX |
+ DexoptOptions.DEXOPT_ONLY_SHARED_DEX |
+ DexoptOptions.DEXOPT_DOWNGRADE |
+ DexoptOptions.DEXOPT_AS_SHARED_LIBRARY |
+ DexoptOptions.DEXOPT_IDLE_BACKGROUND_JOB;
+
+ DexoptOptions opt = new DexoptOptions(mPackageName, mCompilerFilter, flags);
+ assertEquals(mPackageName, opt.getPackageName());
+ assertEquals(mCompilerFilter, opt.getCompilerFilter());
+ assertEquals(null, opt.getSplitName());
+ assertTrue(opt.isBootComplete());
+ assertTrue(opt.isCheckForProfileUpdates());
+ assertTrue(opt.isDexoptOnlySecondaryDex());
+ assertTrue(opt.isDexoptOnlySharedDex());
+ assertTrue(opt.isDowngrade());
+ assertTrue(opt.isForce());
+ assertTrue(opt.isDexoptAsSharedLibrary());
+ assertTrue(opt.isDexoptIdleBackgroundJob());
+ }
+
+ @Test
+ public void testCreateDexoptOptionsReason() {
+ int flags =
+ DexoptOptions.DEXOPT_FORCE |
+ DexoptOptions.DEXOPT_BOOT_COMPLETE |
+ DexoptOptions.DEXOPT_CHECK_FOR_PROFILES_UPDATES;
+
+ int[] reasons = new int[] {
+ PackageManagerService.REASON_FIRST_BOOT,
+ PackageManagerService.REASON_BOOT,
+ PackageManagerService.REASON_INSTALL,
+ PackageManagerService.REASON_BACKGROUND_DEXOPT,
+ PackageManagerService.REASON_AB_OTA,
+ PackageManagerService.REASON_INACTIVE_PACKAGE_DOWNGRADE,};
+
+ for (int reason : reasons) {
+ DexoptOptions opt = new DexoptOptions(mPackageName, reason, flags);
+ assertEquals(mPackageName, opt.getPackageName());
+ assertEquals(getCompilerFilterForReason(reason), opt.getCompilerFilter());
+ assertEquals(null, opt.getSplitName());
+ assertTrue(opt.isBootComplete());
+ assertTrue(opt.isCheckForProfileUpdates());
+ assertFalse(opt.isDexoptOnlySecondaryDex());
+ assertFalse(opt.isDexoptOnlySharedDex());
+ assertFalse(opt.isDowngrade());
+ assertTrue(opt.isForce());
+ assertFalse(opt.isDexoptAsSharedLibrary());
+ }
+ }
+
+ @Test
+ public void testCreateDexoptOptionsSplit() {
+ int flags = DexoptOptions.DEXOPT_FORCE | DexoptOptions.DEXOPT_BOOT_COMPLETE;
+
+ DexoptOptions opt = new DexoptOptions(mPackageName, mCompilerFilter, mSplitName, flags);
+ assertEquals(mPackageName, opt.getPackageName());
+ assertEquals(mCompilerFilter, opt.getCompilerFilter());
+ assertEquals(mSplitName, opt.getSplitName());
+ assertTrue(opt.isBootComplete());
+ assertFalse(opt.isCheckForProfileUpdates());
+ assertFalse(opt.isDexoptOnlySecondaryDex());
+ assertFalse(opt.isDexoptOnlySharedDex());
+ assertFalse(opt.isDowngrade());
+ assertTrue(opt.isForce());
+ assertFalse(opt.isDexoptAsSharedLibrary());
+ }
+
+ @Test
+ public void testCreateDexoptInvalid() {
+ boolean gotException = false;
+ try {
+ int invalidFlags = 999;
+ new DexoptOptions(mPackageName, mCompilerFilter, invalidFlags);
+ } catch (IllegalArgumentException ignore) {
+ gotException = true;
+ }
+
+ assertTrue(gotException);
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/pm/dex/DexoptUtilsTest.java b/services/tests/servicestests/src/com/android/server/pm/dex/DexoptUtilsTest.java
new file mode 100644
index 0000000..34dc1ad
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/pm/dex/DexoptUtilsTest.java
@@ -0,0 +1,420 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.pm.dex;
+
+import com.android.server.pm.PackageDexOptimizer;
+
+import static com.android.server.pm.PackageDexOptimizer.SKIP_SHARED_LIBRARY_CHECK;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import android.content.pm.ApplicationInfo;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.util.SparseArray;
+
+import dalvik.system.DelegateLastClassLoader;
+import dalvik.system.DexClassLoader;
+import dalvik.system.PathClassLoader;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.File;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class DexoptUtilsTest {
+ private static final String DEX_CLASS_LOADER_NAME = DexClassLoader.class.getName();
+ private static final String PATH_CLASS_LOADER_NAME = PathClassLoader.class.getName();
+ private static final String DELEGATE_LAST_CLASS_LOADER_NAME =
+ DelegateLastClassLoader.class.getName();
+
+ private static class TestData {
+ ApplicationInfo info;
+ boolean[] pathsWithCode;
+ }
+
+ private TestData createMockApplicationInfo(String baseClassLoader, boolean addSplits,
+ boolean addSplitDependencies) {
+ ApplicationInfo ai = new ApplicationInfo();
+ String codeDir = "/data/app/mock.android.com";
+ ai.setBaseCodePath(codeDir + "/base.dex");
+ ai.privateFlags = ai.privateFlags | ApplicationInfo.PRIVATE_FLAG_ISOLATED_SPLIT_LOADING;
+ boolean[] pathsWithCode;
+ if (!addSplits) {
+ pathsWithCode = new boolean[] {true};
+ } else {
+ pathsWithCode = new boolean[9];
+ Arrays.fill(pathsWithCode, true);
+ pathsWithCode[7] = false; // config split
+
+ ai.setSplitCodePaths(new String[]{
+ codeDir + "/base-1.dex",
+ codeDir + "/base-2.dex",
+ codeDir + "/base-3.dex",
+ codeDir + "/base-4.dex",
+ codeDir + "/base-5.dex",
+ codeDir + "/base-6.dex",
+ codeDir + "/config-split-7.dex",
+ codeDir + "/feature-no-deps.dex"});
+
+ String[] splitClassLoaderNames = new String[]{
+ PATH_CLASS_LOADER_NAME,
+ PATH_CLASS_LOADER_NAME,
+ PATH_CLASS_LOADER_NAME,
+ PATH_CLASS_LOADER_NAME,
+ PATH_CLASS_LOADER_NAME,
+ null, // A null class loader name should default to PathClassLoader.
+ null, // The config split gets a null class loader.
+ null}; // The feature split with no dependency and no specified class loader.
+ if (addSplitDependencies) {
+ ai.splitDependencies = new SparseArray<>(splitClassLoaderNames.length + 1);
+ ai.splitDependencies.put(0, new int[] {-1}); // base has no dependency
+ ai.splitDependencies.put(1, new int[] {2}); // split 1 depends on 2
+ ai.splitDependencies.put(2, new int[] {4}); // split 2 depends on 4
+ ai.splitDependencies.put(3, new int[] {4}); // split 3 depends on 4
+ ai.splitDependencies.put(4, new int[] {0}); // split 4 depends on base
+ ai.splitDependencies.put(5, new int[] {0}); // split 5 depends on base
+ ai.splitDependencies.put(6, new int[] {5}); // split 6 depends on 5
+ // Do not add the config split to the dependency list.
+ // Do not add the feature split with no dependency to the dependency list.
+ }
+ }
+ TestData data = new TestData();
+ data.info = ai;
+ data.pathsWithCode = pathsWithCode;
+ return data;
+ }
+
+ @Test
+ public void testSplitChain() {
+ TestData data = createMockApplicationInfo(PATH_CLASS_LOADER_NAME, true, true);
+ String[] sharedLibrary = new String[] {"a.dex", "b.dex"};
+ String[] contexts = DexoptUtils.getClassLoaderContexts(
+ data.info, sharedLibrary, data.pathsWithCode);
+
+ assertEquals(9, contexts.length);
+ assertEquals("PCL[a.dex:b.dex]", contexts[0]);
+ assertEquals("PCL[];PCL[base-2.dex];PCL[base-4.dex];PCL[a.dex:b.dex:base.dex]",
+ contexts[1]);
+ assertEquals("PCL[];PCL[base-4.dex];PCL[a.dex:b.dex:base.dex]", contexts[2]);
+ assertEquals("PCL[];PCL[base-4.dex];PCL[a.dex:b.dex:base.dex]", contexts[3]);
+ assertEquals("PCL[];PCL[a.dex:b.dex:base.dex]", contexts[4]);
+ assertEquals("PCL[];PCL[a.dex:b.dex:base.dex]", contexts[5]);
+ assertEquals("PCL[];PCL[base-5.dex];PCL[a.dex:b.dex:base.dex]", contexts[6]);
+ assertEquals(null, contexts[7]); // config split
+ assertEquals("PCL[]", contexts[8]); // feature split with no dependency
+ }
+
+ @Test
+ public void testSplitChainNoSplitDependencies() {
+ TestData data = createMockApplicationInfo(PATH_CLASS_LOADER_NAME, true, false);
+ String[] sharedLibrary = new String[] {"a.dex", "b.dex"};
+ String[] contexts = DexoptUtils.getClassLoaderContexts(
+ data.info, sharedLibrary, data.pathsWithCode);
+
+ assertEquals(9, contexts.length);
+ assertEquals("PCL[a.dex:b.dex]", contexts[0]);
+ assertEquals("PCL[a.dex:b.dex:base.dex]", contexts[1]);
+ assertEquals("PCL[a.dex:b.dex:base.dex:base-1.dex]", contexts[2]);
+ assertEquals("PCL[a.dex:b.dex:base.dex:base-1.dex:base-2.dex]", contexts[3]);
+ assertEquals("PCL[a.dex:b.dex:base.dex:base-1.dex:base-2.dex:base-3.dex]", contexts[4]);
+ assertEquals(
+ "PCL[a.dex:b.dex:base.dex:base-1.dex:base-2.dex:base-3.dex:base-4.dex]",
+ contexts[5]);
+ assertEquals(
+ "PCL[a.dex:b.dex:base.dex:base-1.dex:base-2.dex:base-3.dex:base-4.dex:base-5.dex]",
+ contexts[6]);
+ assertEquals(null, contexts[7]); // config split
+ assertEquals(
+ "PCL[a.dex:b.dex:base.dex:base-1.dex:base-2.dex:base-3.dex:base-4.dex:base-5.dex:base-6.dex:config-split-7.dex]",
+ contexts[8]); // feature split with no dependency
+ }
+
+ @Test
+ public void testSplitChainNoIsolationNoSharedLibrary() {
+ TestData data = createMockApplicationInfo(PATH_CLASS_LOADER_NAME, true, true);
+ data.info.privateFlags = data.info.privateFlags
+ & (~ApplicationInfo.PRIVATE_FLAG_ISOLATED_SPLIT_LOADING);
+ String[] contexts = DexoptUtils.getClassLoaderContexts(
+ data.info, null, data.pathsWithCode);
+
+ assertEquals(9, contexts.length);
+ assertEquals("PCL[]", contexts[0]);
+ assertEquals("PCL[base.dex]", contexts[1]);
+ assertEquals("PCL[base.dex:base-1.dex]", contexts[2]);
+ assertEquals("PCL[base.dex:base-1.dex:base-2.dex]", contexts[3]);
+ assertEquals("PCL[base.dex:base-1.dex:base-2.dex:base-3.dex]", contexts[4]);
+ assertEquals("PCL[base.dex:base-1.dex:base-2.dex:base-3.dex:base-4.dex]", contexts[5]);
+ assertEquals(
+ "PCL[base.dex:base-1.dex:base-2.dex:base-3.dex:base-4.dex:base-5.dex]",
+ contexts[6]);
+ assertEquals(null, contexts[7]); // config split
+ assertEquals(
+ "PCL[base.dex:base-1.dex:base-2.dex:base-3.dex:base-4.dex:base-5.dex:base-6.dex:config-split-7.dex]",
+ contexts[8]); // feature split with no dependency
+ }
+
+ @Test
+ public void testSplitChainNoSharedLibraries() {
+ TestData data = createMockApplicationInfo(
+ DELEGATE_LAST_CLASS_LOADER_NAME, true, true);
+ String[] contexts = DexoptUtils.getClassLoaderContexts(
+ data.info, null, data.pathsWithCode);
+
+ assertEquals(9, contexts.length);
+ assertEquals("PCL[]", contexts[0]);
+ assertEquals("PCL[];PCL[base-2.dex];PCL[base-4.dex];PCL[base.dex]", contexts[1]);
+ assertEquals("PCL[];PCL[base-4.dex];PCL[base.dex]", contexts[2]);
+ assertEquals("PCL[];PCL[base-4.dex];PCL[base.dex]", contexts[3]);
+ assertEquals("PCL[];PCL[base.dex]", contexts[4]);
+ assertEquals("PCL[];PCL[base.dex]", contexts[5]);
+ assertEquals("PCL[];PCL[base-5.dex];PCL[base.dex]", contexts[6]);
+ assertEquals(null, contexts[7]); // config split
+ assertEquals("PCL[]", contexts[8]); // feature split with no dependency
+ }
+
+ @Test
+ public void testSplitChainWithNullPrimaryClassLoader() {
+ // A null classLoaderName should mean PathClassLoader.
+ TestData data = createMockApplicationInfo(null, true, true);
+ String[] sharedLibrary = new String[] {"a.dex", "b.dex"};
+ String[] contexts = DexoptUtils.getClassLoaderContexts(
+ data.info, sharedLibrary, data.pathsWithCode);
+
+ assertEquals(9, contexts.length);
+ assertEquals("PCL[a.dex:b.dex]", contexts[0]);
+ assertEquals("PCL[];PCL[base-2.dex];PCL[base-4.dex];PCL[a.dex:b.dex:base.dex]", contexts[1]);
+ assertEquals("PCL[];PCL[base-4.dex];PCL[a.dex:b.dex:base.dex]", contexts[2]);
+ assertEquals("PCL[];PCL[base-4.dex];PCL[a.dex:b.dex:base.dex]", contexts[3]);
+ assertEquals("PCL[];PCL[a.dex:b.dex:base.dex]", contexts[4]);
+ assertEquals("PCL[];PCL[a.dex:b.dex:base.dex]", contexts[5]);
+ assertEquals("PCL[];PCL[base-5.dex];PCL[a.dex:b.dex:base.dex]", contexts[6]);
+ assertEquals(null, contexts[7]); // config split
+ assertEquals("PCL[]", contexts[8]); // feature split with no dependency
+ }
+
+ @Test
+ public void tesNoSplits() {
+ TestData data = createMockApplicationInfo(PATH_CLASS_LOADER_NAME, false, false);
+ String[] sharedLibrary = new String[] {"a.dex", "b.dex"};
+ String[] contexts = DexoptUtils.getClassLoaderContexts(
+ data.info, sharedLibrary, data.pathsWithCode);
+
+ assertEquals(1, contexts.length);
+ assertEquals("PCL[a.dex:b.dex]", contexts[0]);
+ }
+
+ @Test
+ public void tesNoSplitsNullClassLoaderName() {
+ TestData data = createMockApplicationInfo(null, false, false);
+ String[] sharedLibrary = new String[] {"a.dex", "b.dex"};
+ String[] contexts = DexoptUtils.getClassLoaderContexts(
+ data.info, sharedLibrary, data.pathsWithCode);
+
+ assertEquals(1, contexts.length);
+ assertEquals("PCL[a.dex:b.dex]", contexts[0]);
+ }
+
+ @Test
+ public void tesNoSplitDelegateLast() {
+ TestData data = createMockApplicationInfo(
+ DELEGATE_LAST_CLASS_LOADER_NAME, false, false);
+ String[] sharedLibrary = new String[] {"a.dex", "b.dex"};
+ String[] contexts = DexoptUtils.getClassLoaderContexts(
+ data.info, sharedLibrary, data.pathsWithCode);
+
+ assertEquals(1, contexts.length);
+ assertEquals("PCL[a.dex:b.dex]", contexts[0]);
+ }
+
+ @Test
+ public void tesNoSplitsNoSharedLibraries() {
+ TestData data = createMockApplicationInfo(PATH_CLASS_LOADER_NAME, false, false);
+ String[] contexts = DexoptUtils.getClassLoaderContexts(
+ data.info, null, data.pathsWithCode);
+
+ assertEquals(1, contexts.length);
+ assertEquals("PCL[]", contexts[0]);
+ }
+
+ @Test
+ public void tesNoSplitDelegateLastNoSharedLibraries() {
+ TestData data = createMockApplicationInfo(
+ DELEGATE_LAST_CLASS_LOADER_NAME, false, false);
+ String[] contexts = DexoptUtils.getClassLoaderContexts(
+ data.info, null, data.pathsWithCode);
+
+ assertEquals(1, contexts.length);
+ assertEquals("PCL[]", contexts[0]);
+ }
+
+ @Test
+ public void testContextWithNoCode() {
+ TestData data = createMockApplicationInfo(null, true, false);
+ Arrays.fill(data.pathsWithCode, false);
+
+ String[] sharedLibrary = new String[] {"a.dex", "b.dex"};
+ String[] contexts = DexoptUtils.getClassLoaderContexts(
+ data.info, sharedLibrary, data.pathsWithCode);
+
+ assertEquals(9, contexts.length);
+ assertEquals(null, contexts[0]);
+ assertEquals(null, contexts[1]);
+ assertEquals(null, contexts[2]);
+ assertEquals(null, contexts[3]);
+ assertEquals(null, contexts[4]);
+ assertEquals(null, contexts[5]);
+ assertEquals(null, contexts[6]);
+ assertEquals(null, contexts[7]);
+ }
+
+ @Test
+ public void testContextBaseNoCode() {
+ TestData data = createMockApplicationInfo(null, true, true);
+ data.pathsWithCode[0] = false;
+ String[] sharedLibrary = new String[] {"a.dex", "b.dex"};
+ String[] contexts = DexoptUtils.getClassLoaderContexts(
+ data.info, sharedLibrary, data.pathsWithCode);
+
+ assertEquals(9, contexts.length);
+ assertEquals(null, contexts[0]);
+ assertEquals("PCL[];PCL[base-2.dex];PCL[base-4.dex];PCL[a.dex:b.dex:base.dex]", contexts[1]);
+ assertEquals("PCL[];PCL[base-4.dex];PCL[a.dex:b.dex:base.dex]", contexts[2]);
+ assertEquals("PCL[];PCL[base-4.dex];PCL[a.dex:b.dex:base.dex]", contexts[3]);
+ assertEquals("PCL[];PCL[a.dex:b.dex:base.dex]", contexts[4]);
+ assertEquals("PCL[];PCL[a.dex:b.dex:base.dex]", contexts[5]);
+ assertEquals("PCL[];PCL[base-5.dex];PCL[a.dex:b.dex:base.dex]", contexts[6]);
+ assertEquals(null, contexts[7]);
+ }
+
+ @Test
+ public void testProcessContextForDexLoad() {
+ List<String> classLoaders = Arrays.asList(
+ DELEGATE_LAST_CLASS_LOADER_NAME,
+ PATH_CLASS_LOADER_NAME,
+ PATH_CLASS_LOADER_NAME);
+ List<String> classPaths = Arrays.asList(
+ String.join(File.pathSeparator, "foo.dex", "bar.dex"),
+ String.join(File.pathSeparator, "parent1.dex"),
+ String.join(File.pathSeparator, "parent2.dex", "parent3.dex"));
+ String[] context = DexoptUtils.processContextForDexLoad(classLoaders, classPaths);
+ assertNotNull(context);
+ assertEquals(2, context.length);
+ assertEquals("PCL[];PCL[parent1.dex];PCL[parent2.dex:parent3.dex]", context[0]);
+ assertEquals("PCL[foo.dex];PCL[parent1.dex];PCL[parent2.dex:parent3.dex]", context[1]);
+ }
+
+ @Test
+ public void testProcessContextForDexLoadSingleElement() {
+ List<String> classLoaders = Arrays.asList(PATH_CLASS_LOADER_NAME);
+ List<String> classPaths = Arrays.asList(
+ String.join(File.pathSeparator, "foo.dex", "bar.dex", "zoo.dex"));
+ String[] context = DexoptUtils.processContextForDexLoad(classLoaders, classPaths);
+ assertNotNull(context);
+ assertEquals(3, context.length);
+ assertEquals("PCL[]", context[0]);
+ assertEquals("PCL[foo.dex]", context[1]);
+ assertEquals("PCL[foo.dex:bar.dex]", context[2]);
+ }
+
+ @Test
+ public void testProcessContextForDexLoadUnsupported() {
+ List<String> classLoaders = Arrays.asList(
+ DELEGATE_LAST_CLASS_LOADER_NAME,
+ "unsupported.class.loader");
+ List<String> classPaths = Arrays.asList(
+ String.join(File.pathSeparator, "foo.dex", "bar.dex"),
+ String.join(File.pathSeparator, "parent1.dex"));
+ String[] context = DexoptUtils.processContextForDexLoad(classLoaders, classPaths);
+ assertNull(context);
+ }
+
+ @Test
+ public void testProcessContextForDexLoadIllegalCallEmptyList() {
+ boolean gotException = false;
+ try {
+ DexoptUtils.processContextForDexLoad(Collections.emptyList(), Collections.emptyList());
+ } catch (IllegalArgumentException ignore) {
+ gotException = true;
+ }
+ assertTrue(gotException);
+ }
+
+ @Test
+ public void testProcessContextForDexLoadIllegalCallDifferentSize() {
+ boolean gotException = false;
+ try {
+ DexoptUtils.processContextForDexLoad(Collections.emptyList(), Arrays.asList("a"));
+ } catch (IllegalArgumentException ignore) {
+ gotException = true;
+ }
+ assertTrue(gotException);
+ }
+
+ @Test
+ public void testEncodeClassLoader() {
+ assertEquals(SKIP_SHARED_LIBRARY_CHECK, DexoptUtils.encodeClassLoader(
+ SKIP_SHARED_LIBRARY_CHECK, "dalvik.system.PathClassLoader"));
+ assertEquals(SKIP_SHARED_LIBRARY_CHECK, DexoptUtils.encodeClassLoader(
+ SKIP_SHARED_LIBRARY_CHECK, "dalvik.system.DexClassLoader"));
+ assertEquals(SKIP_SHARED_LIBRARY_CHECK, DexoptUtils.encodeClassLoader(
+ SKIP_SHARED_LIBRARY_CHECK, "dalvik.system.DelegateLastClassLoader"));
+ assertEquals("PCL[xyz]", DexoptUtils.encodeClassLoader("xyz",
+ "dalvik.system.PathClassLoader"));
+ assertEquals("PCL[xyz]", DexoptUtils.encodeClassLoader("xyz",
+ "dalvik.system.DexClassLoader"));
+ assertEquals("DLC[xyz]", DexoptUtils.encodeClassLoader("xyz",
+ "dalvik.system.DelegateLastClassLoader"));
+ assertEquals("PCL[xyz]", DexoptUtils.encodeClassLoader("xyz", null));
+ assertEquals("abc[xyz]", DexoptUtils.encodeClassLoader("xyz", "abc"));
+
+ try {
+ DexoptUtils.encodeClassLoader(null, "abc");
+ fail(); // Exception should be caught.
+ } catch (NullPointerException expected) {}
+ }
+
+ @Test
+ public void testEncodeClassLoaderChain() {
+ assertEquals(SKIP_SHARED_LIBRARY_CHECK, DexoptUtils.encodeClassLoaderChain(
+ SKIP_SHARED_LIBRARY_CHECK, "PCL[a]"));
+ assertEquals(SKIP_SHARED_LIBRARY_CHECK, DexoptUtils.encodeClassLoaderChain("PCL[a]",
+ SKIP_SHARED_LIBRARY_CHECK));
+ assertEquals("PCL[a];DLC[b]", DexoptUtils.encodeClassLoaderChain("PCL[a]",
+ "DLC[b]"));
+ assertEquals(SKIP_SHARED_LIBRARY_CHECK, DexoptUtils.encodeClassLoaderChain("PCL[a]",
+ SKIP_SHARED_LIBRARY_CHECK));
+
+ try {
+ DexoptUtils.encodeClassLoaderChain("a", null);
+ fail(); // exception is expected
+ } catch (NullPointerException expected) {}
+
+ try {
+ DexoptUtils.encodeClassLoaderChain(null, "b");
+ fail(); // exception is expected
+ } catch (NullPointerException expected) {}
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/pm/dex/PackageDexUsageTests.java b/services/tests/servicestests/src/com/android/server/pm/dex/PackageDexUsageTests.java
index 2e99433..69a148d 100644
--- a/services/tests/servicestests/src/com/android/server/pm/dex/PackageDexUsageTests.java
+++ b/services/tests/servicestests/src/com/android/server/pm/dex/PackageDexUsageTests.java
@@ -21,6 +21,7 @@
import android.support.test.runner.AndroidJUnit4;
import dalvik.system.VMRuntime;
+import java.util.Collections;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -72,25 +73,25 @@
String isa = VMRuntime.getInstructionSet(Build.SUPPORTED_ABIS[0]);
mFooBaseUser0 = new TestData(fooPackageName,
- fooCodeDir + "base.apk", 0, isa, false, true);
+ fooCodeDir + "base.apk", 0, isa, false, true, fooPackageName);
mFooSplit1User0 = new TestData(fooPackageName,
- fooCodeDir + "split-1.apk", 0, isa, false, true);
+ fooCodeDir + "split-1.apk", 0, isa, false, true, fooPackageName);
mFooSplit2UsedByOtherApps0 = new TestData(fooPackageName,
- fooCodeDir + "split-2.apk", 0, isa, true, true);
+ fooCodeDir + "split-2.apk", 0, isa, true, true, "used.by.other.com");
mFooSecondary1User0 = new TestData(fooPackageName,
- fooDataDir + "sec-1.dex", 0, isa, false, false);
+ fooDataDir + "sec-1.dex", 0, isa, false, false, fooPackageName);
mFooSecondary1User1 = new TestData(fooPackageName,
- fooDataDir + "sec-1.dex", 1, isa, false, false);
+ fooDataDir + "sec-1.dex", 1, isa, false, false, fooPackageName);
mFooSecondary2UsedByOtherApps0 = new TestData(fooPackageName,
- fooDataDir + "sec-2.dex", 0, isa, true, false);
+ fooDataDir + "sec-2.dex", 0, isa, true, false, "used.by.other.com");
mInvalidIsa = new TestData(fooPackageName,
- fooCodeDir + "base.apk", 0, "INVALID_ISA", false, true);
+ fooCodeDir + "base.apk", 0, "INVALID_ISA", false, true, "INALID_USER");
String barPackageName = "com.google.bar";
String barCodeDir = "/data/app/com.google.bar/";
@@ -98,11 +99,11 @@
String barDataDir1 = "/data/user/1/com.google.bar/";
mBarBaseUser0 = new TestData(barPackageName,
- barCodeDir + "base.apk", 0, isa, false, true);
+ barCodeDir + "base.apk", 0, isa, false, true, barPackageName);
mBarSecondary1User0 = new TestData(barPackageName,
- barDataDir + "sec-1.dex", 0, isa, false, false);
+ barDataDir + "sec-1.dex", 0, isa, false, false, barPackageName);
mBarSecondary2User1 = new TestData(barPackageName,
- barDataDir1 + "sec-2.dex", 1, isa, false, false);
+ barDataDir1 + "sec-2.dex", 1, isa, false, false, barPackageName);
}
@Test
@@ -249,7 +250,10 @@
Map<String, Set<Integer>> packageToUsersMap = new HashMap<>();
packageToUsersMap.put(mBarSecondary2User1.mPackageName,
new HashSet<>(Arrays.asList(mBarSecondary2User1.mOwnerUserId)));
- mPackageDexUsage.syncData(packageToUsersMap);
+ Map<String, Set<String>> packageToCodePaths = new HashMap<>();
+ packageToCodePaths.put(mBarBaseUser0.mPackageName,
+ new HashSet<>(Arrays.asList(mBarBaseUser0.mDexFile)));
+ mPackageDexUsage.syncData(packageToUsersMap, packageToCodePaths);
// Assert that only user 1 files are there.
assertPackageDexUsage(mBarBaseUser0, mBarSecondary2User1);
@@ -319,7 +323,8 @@
mFooSplit2UsedByOtherApps0.mOwnerUserId,
mFooSplit2UsedByOtherApps0.mLoaderIsa,
/*mIsUsedByOtherApps*/false,
- mFooSplit2UsedByOtherApps0.mPrimaryOrSplit);
+ mFooSplit2UsedByOtherApps0.mPrimaryOrSplit,
+ mFooSplit2UsedByOtherApps0.mUsedBy);
assertPackageDexUsage(noLongerUsedByOtherApps);
}
@@ -332,14 +337,223 @@
assertFalse(mPackageDexUsage.clearUsedByOtherApps(mFooSplit2UsedByOtherApps0.mPackageName));
}
+ @Test
+ public void testRecordDexFileUsers() {
+ PackageDexUsage packageDexUsageRecordUsers = new PackageDexUsage();
+ Set<String> users = new HashSet<>(Arrays.asList(
+ new String[] {"another.package.1"}));
+ Set<String> usersExtra = new HashSet<>(Arrays.asList(
+ new String[] {"another.package.2", "another.package.3"}));
+
+ assertTrue(record(packageDexUsageRecordUsers, mFooSplit2UsedByOtherApps0, users));
+ assertTrue(record(packageDexUsageRecordUsers, mFooSplit2UsedByOtherApps0, usersExtra));
+
+ assertTrue(record(packageDexUsageRecordUsers, mFooSecondary1User0, users));
+ assertTrue(record(packageDexUsageRecordUsers, mFooSecondary1User0, usersExtra));
+
+ packageDexUsageRecordUsers = writeAndReadBack(packageDexUsageRecordUsers);
+ // Verify that the users were recorded.
+ Set<String> userAll = new HashSet<>(users);
+ userAll.addAll(usersExtra);
+ assertPackageDexUsage(packageDexUsageRecordUsers, userAll, mFooSplit2UsedByOtherApps0,
+ mFooSecondary1User0);
+ }
+
+ @Test
+ public void testRecordDexFileUsersNotTheOwningPackage() {
+ PackageDexUsage packageDexUsageRecordUsers = new PackageDexUsage();
+ Set<String> users = new HashSet<>(Arrays.asList(
+ new String[] {mFooSplit2UsedByOtherApps0.mPackageName}));
+ Set<String> usersExtra = new HashSet<>(Arrays.asList(
+ new String[] {"another.package.2", "another.package.3"}));
+
+ assertTrue(record(packageDexUsageRecordUsers, mFooSplit2UsedByOtherApps0, users));
+ assertTrue(record(packageDexUsageRecordUsers, mFooSplit2UsedByOtherApps0, usersExtra));
+
+ assertTrue(record(packageDexUsageRecordUsers, mFooSecondary1User0, users));
+ assertTrue(record(packageDexUsageRecordUsers, mFooSecondary1User0, usersExtra));
+
+ packageDexUsageRecordUsers = writeAndReadBack(packageDexUsageRecordUsers);
+ // Verify that only the non owning packages were recorded.
+ assertPackageDexUsage(packageDexUsageRecordUsers, usersExtra, mFooSplit2UsedByOtherApps0,
+ mFooSecondary1User0);
+ }
+
+ @Test
+ public void testRecordClassLoaderContextVariableContext() {
+ // Record a secondary dex file.
+ assertTrue(record(mFooSecondary1User0));
+ // Now update its context.
+ TestData fooSecondary1User0NewContext = mFooSecondary1User0.updateClassLoaderContext(
+ "PCL[new_context.dex]");
+ assertTrue(record(fooSecondary1User0NewContext));
+
+ // Not check that the context was switch to variable.
+ TestData expectedContext = mFooSecondary1User0.updateClassLoaderContext(
+ PackageDexUsage.VARIABLE_CLASS_LOADER_CONTEXT);
+
+ assertPackageDexUsage(null, expectedContext);
+ writeAndReadBack();
+ assertPackageDexUsage(null, expectedContext);
+ }
+
+ @Test
+ public void testRecordClassLoaderContextUnsupportedContext() {
+ // Record a secondary dex file.
+ assertTrue(record(mFooSecondary1User0));
+ // Now update its context.
+ TestData unsupportedContext = mFooSecondary1User0.updateClassLoaderContext(
+ PackageDexUsage.UNSUPPORTED_CLASS_LOADER_CONTEXT);
+ assertTrue(record(unsupportedContext));
+
+ assertPackageDexUsage(null, unsupportedContext);
+ writeAndReadBack();
+ assertPackageDexUsage(null, unsupportedContext);
+ }
+
+ @Test
+ public void testRecordClassLoaderContextTransitionFromUnknown() {
+ // Record a secondary dex file.
+ TestData unknownContext = mFooSecondary1User0.updateClassLoaderContext(
+ PackageDexUsage.UNKNOWN_CLASS_LOADER_CONTEXT);
+ assertTrue(record(unknownContext));
+
+ assertPackageDexUsage(null, unknownContext);
+ writeAndReadBack();
+ assertPackageDexUsage(null, unknownContext);
+
+ // Now update the secondary dex record with a class loader context. This simulates the
+ // version 2 to version 3 upgrade.
+
+ assertTrue(record(mFooSecondary1User0));
+
+ assertPackageDexUsage(null, mFooSecondary1User0);
+ writeAndReadBack();
+ assertPackageDexUsage(null, mFooSecondary1User0);
+ }
+
+ @Test
+ public void testDexUsageClassLoaderContext() {
+ final boolean isUsedByOtherApps = false;
+ final int userId = 0;
+ PackageDexUsage.DexUseInfo validContext = new DexUseInfo(isUsedByOtherApps, userId,
+ "valid_context", "arm");
+ assertFalse(validContext.isUnknownClassLoaderContext());
+ assertFalse(validContext.isUnsupportedClassLoaderContext());
+ assertFalse(validContext.isVariableClassLoaderContext());
+
+ PackageDexUsage.DexUseInfo unsupportedContext = new DexUseInfo(isUsedByOtherApps, userId,
+ PackageDexUsage.UNSUPPORTED_CLASS_LOADER_CONTEXT, "arm");
+ assertFalse(unsupportedContext.isUnknownClassLoaderContext());
+ assertTrue(unsupportedContext.isUnsupportedClassLoaderContext());
+ assertFalse(unsupportedContext.isVariableClassLoaderContext());
+
+ PackageDexUsage.DexUseInfo variableContext = new DexUseInfo(isUsedByOtherApps, userId,
+ PackageDexUsage.VARIABLE_CLASS_LOADER_CONTEXT, "arm");
+ assertFalse(variableContext.isUnknownClassLoaderContext());
+ assertFalse(variableContext.isUnsupportedClassLoaderContext());
+ assertTrue(variableContext.isVariableClassLoaderContext());
+
+ PackageDexUsage.DexUseInfo unknownContext = new DexUseInfo(isUsedByOtherApps, userId,
+ PackageDexUsage.UNKNOWN_CLASS_LOADER_CONTEXT, "arm");
+ assertTrue(unknownContext.isUnknownClassLoaderContext());
+ assertFalse(unknownContext.isUnsupportedClassLoaderContext());
+ assertFalse(unknownContext.isVariableClassLoaderContext());
+ }
+
+ @Test
+ public void testReadVersion1() {
+ String isa = VMRuntime.getInstructionSet(Build.SUPPORTED_ABIS[0]);
+ // Equivalent to
+ // record(mFooSplit2UsedByOtherApps0);
+ // record(mFooSecondary1User0);
+ // record(mFooSecondary2UsedByOtherApps0);
+ // record(mBarBaseUser0);
+ // record(mBarSecondary1User0);
+ String content = "PACKAGE_MANAGER__PACKAGE_DEX_USAGE__1\n"
+ + "com.google.foo,1\n"
+ + "#/data/user/0/com.google.foo/sec-1.dex\n"
+ + "0,0," + isa + "\n"
+ + "#/data/user/0/com.google.foo/sec-2.dex\n"
+ + "0,1," + isa + "\n"
+ + "com.google.bar,0\n"
+ + "#/data/user/0/com.google.bar/sec-1.dex\n"
+ + "0,0," + isa + "\n";
+
+ PackageDexUsage packageDexUsage = new PackageDexUsage();
+ try {
+ packageDexUsage.read(new StringReader(content));
+ } catch (IOException e) {
+ fail();
+ }
+
+ // After the read we must sync the data to fill the missing information on the code paths.
+ Map<String, Set<Integer>> packageToUsersMap = new HashMap<>();
+ Map<String, Set<String>> packageToCodePaths = new HashMap<>();
+
+ // Handle foo package.
+ packageToUsersMap.put(mFooSplit2UsedByOtherApps0.mPackageName,
+ new HashSet<>(Arrays.asList(mFooSplit2UsedByOtherApps0.mOwnerUserId)));
+ packageToCodePaths.put(mFooSplit2UsedByOtherApps0.mPackageName,
+ new HashSet<>(Arrays.asList(mFooSplit2UsedByOtherApps0.mDexFile,
+ mFooSplit1User0.mDexFile, mFooBaseUser0.mDexFile)));
+ // Handle bar package.
+ packageToUsersMap.put(mBarBaseUser0.mPackageName,
+ new HashSet<>(Arrays.asList(mBarBaseUser0.mOwnerUserId)));
+ packageToCodePaths.put(mBarBaseUser0.mPackageName,
+ new HashSet<>(Arrays.asList(mBarBaseUser0.mDexFile)));
+
+ // Sync the data.
+ packageDexUsage.syncData(packageToUsersMap, packageToCodePaths);
+
+ // Update the class loaders to unknown before asserting if needed. Before version 2 we
+ // didn't have any.
+ String unknown = PackageDexUsage.UNKNOWN_CLASS_LOADER_CONTEXT;
+ TestData fooBaseUser0 = mFooBaseUser0.updateClassLoaderContext(unknown);
+ TestData fooSplit1User0 = mFooSplit1User0.updateClassLoaderContext(unknown);
+ TestData fooSplit2UsedByOtherApps0 =
+ mFooSplit2UsedByOtherApps0.updateClassLoaderContext(unknown);
+ TestData fooSecondary1User0 = mFooSecondary1User0.updateClassLoaderContext(unknown);
+ TestData fooSecondary2UsedByOtherApps0 =
+ mFooSecondary2UsedByOtherApps0.updateClassLoaderContext(unknown);
+ TestData barBaseUser0 = mBarBaseUser0.updateClassLoaderContext(unknown);
+ TestData barSecondary1User0 = mBarSecondary1User0.updateClassLoaderContext(unknown);
+
+ // Assert foo code paths. Note that we ignore the users during upgrade.
+ final Set<String> ignoredUsers = null;
+ assertPackageDexUsage(packageDexUsage, ignoredUsers,
+ fooSplit2UsedByOtherApps0, fooSecondary1User0, fooSecondary2UsedByOtherApps0);
+ // Because fooSplit2UsedByOtherApps0 is used by others, all the other code paths must
+ // share the same data.
+ assertPackageDexUsage(packageDexUsage, ignoredUsers,
+ fooSplit1User0.updateUseByOthers(true),
+ fooSecondary1User0, fooSecondary2UsedByOtherApps0);
+ assertPackageDexUsage(packageDexUsage, ignoredUsers, fooBaseUser0.updateUseByOthers(true),
+ fooSecondary1User0, fooSecondary2UsedByOtherApps0);
+
+ // Assert bar code paths. Note that we ignore the users during upgrade.
+ assertPackageDexUsage(packageDexUsage, ignoredUsers, barBaseUser0, barSecondary1User0);
+ }
+
private void assertPackageDexUsage(TestData primary, TestData... secondaries) {
+ assertPackageDexUsage(mPackageDexUsage, null, primary, secondaries);
+ }
+
+ private void assertPackageDexUsage(PackageDexUsage packageDexUsage, Set<String> users,
+ TestData primary, TestData... secondaries) {
String packageName = primary == null ? secondaries[0].mPackageName : primary.mPackageName;
- boolean primaryUsedByOtherApps = primary == null ? false : primary.mUsedByOtherApps;
- PackageUseInfo pInfo = mPackageDexUsage.getPackageUseInfo(packageName);
+ boolean primaryUsedByOtherApps = primary != null && primary.mUsedByOtherApps;
+ PackageUseInfo pInfo = packageDexUsage.getPackageUseInfo(packageName);
// Check package use info
assertNotNull(pInfo);
- assertEquals(primaryUsedByOtherApps, pInfo.isUsedByOtherApps());
+ if (primary != null) {
+ assertEquals(primaryUsedByOtherApps, pInfo.isUsedByOtherApps(primary.mDexFile));
+ if (users != null) {
+ assertEquals(pInfo.getLoadingPackages(primary.mDexFile), users);
+ }
+ }
+
Map<String, DexUseInfo> dexUseInfoMap = pInfo.getDexUseInfoMap();
assertEquals(secondaries.length, dexUseInfoMap.size());
@@ -351,24 +565,45 @@
assertEquals(testData.mOwnerUserId, dInfo.getOwnerUserId());
assertEquals(1, dInfo.getLoaderIsas().size());
assertTrue(dInfo.getLoaderIsas().contains(testData.mLoaderIsa));
+ if (users != null) {
+ assertEquals(dInfo.getLoadingPackages(), users);
+ }
+
+ assertEquals(testData.mClassLoaderContext, dInfo.getClassLoaderContext());
}
}
private boolean record(TestData testData) {
return mPackageDexUsage.record(testData.mPackageName, testData.mDexFile,
- testData.mOwnerUserId, testData.mLoaderIsa, testData.mUsedByOtherApps,
- testData.mPrimaryOrSplit);
+ testData.mOwnerUserId, testData.mLoaderIsa, testData.mUsedByOtherApps,
+ testData.mPrimaryOrSplit, testData.mUsedBy, testData.mClassLoaderContext);
+ }
+
+ private boolean record(PackageDexUsage packageDexUsage, TestData testData, Set<String> users) {
+ boolean result = true;
+ for (String user : users) {
+ result = result && packageDexUsage.record(testData.mPackageName, testData.mDexFile,
+ testData.mOwnerUserId, testData.mLoaderIsa, testData.mUsedByOtherApps,
+ testData.mPrimaryOrSplit, user, testData.mClassLoaderContext);
+ }
+ return result;
}
private void writeAndReadBack() {
+ mPackageDexUsage = writeAndReadBack(mPackageDexUsage);
+ }
+
+ private PackageDexUsage writeAndReadBack(PackageDexUsage packageDexUsage) {
try {
StringWriter writer = new StringWriter();
- mPackageDexUsage.write(writer);
+ packageDexUsage.write(writer);
- mPackageDexUsage = new PackageDexUsage();
- mPackageDexUsage.read(new StringReader(writer.toString()));
+ PackageDexUsage newPackageDexUsage = new PackageDexUsage();
+ newPackageDexUsage.read(new StringReader(writer.toString()));
+ return newPackageDexUsage;
} catch (IOException e) {
fail("Unexpected IOException: " + e.getMessage());
+ return null;
}
}
@@ -379,16 +614,35 @@
private final String mLoaderIsa;
private final boolean mUsedByOtherApps;
private final boolean mPrimaryOrSplit;
+ private final String mUsedBy;
+ private final String mClassLoaderContext;
private TestData(String packageName, String dexFile, int ownerUserId,
- String loaderIsa, boolean isUsedByOtherApps, boolean primaryOrSplit) {
+ String loaderIsa, boolean isUsedByOtherApps, boolean primaryOrSplit, String usedBy) {
+ this(packageName, dexFile, ownerUserId, loaderIsa, isUsedByOtherApps, primaryOrSplit,
+ usedBy, "DefaultClassLoaderContextFor_" + dexFile);
+ }
+ private TestData(String packageName, String dexFile, int ownerUserId,
+ String loaderIsa, boolean isUsedByOtherApps, boolean primaryOrSplit, String usedBy,
+ String classLoaderContext) {
mPackageName = packageName;
mDexFile = dexFile;
mOwnerUserId = ownerUserId;
mLoaderIsa = loaderIsa;
mUsedByOtherApps = isUsedByOtherApps;
mPrimaryOrSplit = primaryOrSplit;
+ mUsedBy = usedBy;
+ mClassLoaderContext = classLoaderContext;
}
+ private TestData updateClassLoaderContext(String newContext) {
+ return new TestData(mPackageName, mDexFile, mOwnerUserId, mLoaderIsa, mUsedByOtherApps,
+ mPrimaryOrSplit, mUsedBy, newContext);
+ }
+
+ private TestData updateUseByOthers(boolean newUsedByOthers) {
+ return new TestData(mPackageName, mDexFile, mOwnerUserId, mLoaderIsa, newUsedByOthers,
+ mPrimaryOrSplit, mUsedBy, mClassLoaderContext);
+ }
}
}
diff --git a/services/usage/java/com/android/server/usage/StorageStatsService.java b/services/usage/java/com/android/server/usage/StorageStatsService.java
index 9f4fb85..21b11b0 100644
--- a/services/usage/java/com/android/server/usage/StorageStatsService.java
+++ b/services/usage/java/com/android/server/usage/StorageStatsService.java
@@ -392,6 +392,7 @@
res.videoBytes = stats[2];
res.imageBytes = stats[3];
res.appBytes = stats[4];
+ res.obbBytes = stats[5];
return res;
}
diff --git a/services/usb/java/com/android/server/usb/UsbDeviceManager.java b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
index 2219863..946d237 100644
--- a/services/usb/java/com/android/server/usb/UsbDeviceManager.java
+++ b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
@@ -902,7 +902,7 @@
updateCurrentAccessory();
}
if (mBootCompleted) {
- if (!mConnected) {
+ if (!mConnected && !hasMessages(MSG_ACCESSORY_MODE_ENTER_TIMEOUT)) {
// restore defaults when USB is disconnected
setEnabledFunctions(null, !mAdbEnabled, false);
}
diff --git a/telecomm/java/android/telecom/CallAudioState.java b/telecomm/java/android/telecom/CallAudioState.java
index f601d8b..4b827d2 100644
--- a/telecomm/java/android/telecom/CallAudioState.java
+++ b/telecomm/java/android/telecom/CallAudioState.java
@@ -16,16 +16,35 @@
package android.telecom;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.bluetooth.BluetoothDevice;
import android.os.Parcel;
import android.os.Parcelable;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
import java.util.Locale;
+import java.util.Objects;
+import java.util.stream.Collectors;
/**
* Encapsulates the telecom audio state, including the current audio routing, supported audio
* routing and mute.
*/
public final class CallAudioState implements Parcelable {
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(value={ROUTE_EARPIECE, ROUTE_BLUETOOTH, ROUTE_WIRED_HEADSET, ROUTE_SPEAKER},
+ flag=true)
+ public @interface CallAudioRoute {}
+
/** Direct the audio stream through the device's earpiece. */
public static final int ROUTE_EARPIECE = 0x00000001;
@@ -55,6 +74,8 @@
private final boolean isMuted;
private final int route;
private final int supportedRouteMask;
+ private final BluetoothDevice activeBluetoothDevice;
+ private final Collection<BluetoothDevice> supportedBluetoothDevices;
/**
* Constructor for a {@link CallAudioState} object.
@@ -73,10 +94,21 @@
* {@link #ROUTE_WIRED_HEADSET}
* {@link #ROUTE_SPEAKER}
*/
- public CallAudioState(boolean muted, int route, int supportedRouteMask) {
- this.isMuted = muted;
+ public CallAudioState(boolean muted, @CallAudioRoute int route,
+ @CallAudioRoute int supportedRouteMask) {
+ this(muted, route, supportedRouteMask, null, Collections.emptyList());
+ }
+
+ /** @hide */
+ public CallAudioState(boolean isMuted, @CallAudioRoute int route,
+ @CallAudioRoute int supportedRouteMask,
+ @Nullable BluetoothDevice activeBluetoothDevice,
+ @NonNull Collection<BluetoothDevice> supportedBluetoothDevices) {
+ this.isMuted = isMuted;
this.route = route;
this.supportedRouteMask = supportedRouteMask;
+ this.activeBluetoothDevice = activeBluetoothDevice;
+ this.supportedBluetoothDevices = supportedBluetoothDevices;
}
/** @hide */
@@ -84,6 +116,8 @@
isMuted = state.isMuted();
route = state.getRoute();
supportedRouteMask = state.getSupportedRouteMask();
+ activeBluetoothDevice = state.activeBluetoothDevice;
+ supportedBluetoothDevices = state.getSupportedBluetoothDevices();
}
/** @hide */
@@ -92,6 +126,8 @@
isMuted = state.isMuted();
route = state.getRoute();
supportedRouteMask = state.getSupportedRouteMask();
+ activeBluetoothDevice = null;
+ supportedBluetoothDevices = Collections.emptyList();
}
@Override
@@ -103,17 +139,32 @@
return false;
}
CallAudioState state = (CallAudioState) obj;
- return isMuted() == state.isMuted() && getRoute() == state.getRoute() &&
- getSupportedRouteMask() == state.getSupportedRouteMask();
+ if (supportedBluetoothDevices.size() != state.supportedBluetoothDevices.size()) {
+ return false;
+ }
+ for (BluetoothDevice device : supportedBluetoothDevices) {
+ if (!state.supportedBluetoothDevices.contains(device)) {
+ return false;
+ }
+ }
+ return Objects.equals(activeBluetoothDevice, state.activeBluetoothDevice) && isMuted() ==
+ state.isMuted() && getRoute() == state.getRoute() && getSupportedRouteMask() ==
+ state.getSupportedRouteMask();
}
@Override
public String toString() {
+ String bluetoothDeviceList = supportedBluetoothDevices.stream()
+ .map(BluetoothDevice::getAddress).collect(Collectors.joining(", "));
+
return String.format(Locale.US,
- "[AudioState isMuted: %b, route: %s, supportedRouteMask: %s]",
+ "[AudioState isMuted: %b, route: %s, supportedRouteMask: %s, " +
+ "activeBluetoothDevice: [%s], supportedBluetoothDevices: [%s]]",
isMuted,
audioRouteToString(route),
- audioRouteToString(supportedRouteMask));
+ audioRouteToString(supportedRouteMask),
+ activeBluetoothDevice,
+ bluetoothDeviceList);
}
/**
@@ -126,6 +177,7 @@
/**
* @return The current audio route being used.
*/
+ @CallAudioRoute
public int getRoute() {
return route;
}
@@ -133,11 +185,27 @@
/**
* @return Bit mask of all routes supported by this call.
*/
+ @CallAudioRoute
public int getSupportedRouteMask() {
return supportedRouteMask;
}
/**
+ * @return The {@link BluetoothDevice} through which audio is being routed.
+ * Will not be {@code null} if {@link #getRoute()} returns {@link #ROUTE_BLUETOOTH}.
+ */
+ public BluetoothDevice getActiveBluetoothDevice() {
+ return activeBluetoothDevice;
+ }
+
+ /**
+ * @return {@link List} of {@link BluetoothDevice}s that can be used for this call.
+ */
+ public Collection<BluetoothDevice> getSupportedBluetoothDevices() {
+ return supportedBluetoothDevices;
+ }
+
+ /**
* Converts the provided audio route into a human readable string representation.
*
* @param route to convert into a string.
@@ -177,7 +245,13 @@
boolean isMuted = source.readByte() == 0 ? false : true;
int route = source.readInt();
int supportedRouteMask = source.readInt();
- return new CallAudioState(isMuted, route, supportedRouteMask);
+ BluetoothDevice activeBluetoothDevice = source.readParcelable(
+ ClassLoader.getSystemClassLoader());
+ List<BluetoothDevice> supportedBluetoothDevices = new ArrayList<>();
+ source.readParcelableList(supportedBluetoothDevices,
+ ClassLoader.getSystemClassLoader());
+ return new CallAudioState(isMuted, route,
+ supportedRouteMask, activeBluetoothDevice, supportedBluetoothDevices);
}
@Override
@@ -202,6 +276,8 @@
destination.writeByte((byte) (isMuted ? 1 : 0));
destination.writeInt(route);
destination.writeInt(supportedRouteMask);
+ destination.writeParcelable(activeBluetoothDevice, 0);
+ destination.writeParcelableList(new ArrayList<>(supportedBluetoothDevices), 0);
}
private static void listAppend(StringBuffer buffer, String str) {
diff --git a/telecomm/java/android/telecom/Connection.java b/telecomm/java/android/telecom/Connection.java
index 8ba934c..ffb5e93 100644
--- a/telecomm/java/android/telecom/Connection.java
+++ b/telecomm/java/android/telecom/Connection.java
@@ -25,6 +25,7 @@
import android.annotation.SystemApi;
import android.annotation.TestApi;
import android.app.Notification;
+import android.bluetooth.BluetoothDevice;
import android.content.Intent;
import android.hardware.camera2.CameraManager;
import android.net.Uri;
@@ -819,7 +820,7 @@
public void onConnectionEvent(Connection c, String event, Bundle extras) {}
/** @hide */
public void onConferenceSupportedChanged(Connection c, boolean isConferenceSupported) {}
- public void onAudioRouteChanged(Connection c, int audioRoute) {}
+ public void onAudioRouteChanged(Connection c, int audioRoute, String bluetoothAddress) {}
public void onRttInitiationSuccess(Connection c) {}
public void onRttInitiationFailure(Connection c, int reason) {}
public void onRttSessionRemotelyTerminated(Connection c) {}
@@ -2576,7 +2577,29 @@
*/
public final void setAudioRoute(int route) {
for (Listener l : mListeners) {
- l.onAudioRouteChanged(this, route);
+ l.onAudioRouteChanged(this, route, null);
+ }
+ }
+
+ /**
+ *
+ * Request audio routing to a specific bluetooth device. Calling this method may result in
+ * the device routing audio to a different bluetooth device than the one specified if the
+ * bluetooth stack is unable to route audio to the requested device.
+ * A list of available devices can be obtained via
+ * {@link CallAudioState#getSupportedBluetoothDevices()}
+ *
+ * <p>
+ * Used by self-managed {@link ConnectionService}s which wish to use bluetooth audio for a
+ * self-managed {@link Connection} (see {@link PhoneAccount#CAPABILITY_SELF_MANAGED}.)
+ * <p>
+ * See also {@link InCallService#requestBluetoothAudio(String)}
+ * @param bluetoothAddress The address of the bluetooth device to connect to, as returned by
+ * {@link BluetoothDevice#getAddress()}.
+ */
+ public void requestBluetoothAudio(@NonNull String bluetoothAddress) {
+ for (Listener l : mListeners) {
+ l.onAudioRouteChanged(this, CallAudioState.ROUTE_BLUETOOTH, bluetoothAddress);
}
}
diff --git a/telecomm/java/android/telecom/ConnectionService.java b/telecomm/java/android/telecom/ConnectionService.java
index a81fba9..35804f6 100644
--- a/telecomm/java/android/telecom/ConnectionService.java
+++ b/telecomm/java/android/telecom/ConnectionService.java
@@ -1294,10 +1294,10 @@
}
@Override
- public void onAudioRouteChanged(Connection c, int audioRoute) {
+ public void onAudioRouteChanged(Connection c, int audioRoute, String bluetoothAddress) {
String id = mIdByConnection.get(c);
if (id != null) {
- mAdapter.setAudioRoute(id, audioRoute);
+ mAdapter.setAudioRoute(id, audioRoute, bluetoothAddress);
}
}
diff --git a/telecomm/java/android/telecom/ConnectionServiceAdapter.java b/telecomm/java/android/telecom/ConnectionServiceAdapter.java
index 111fcc7..92a9dc2 100644
--- a/telecomm/java/android/telecom/ConnectionServiceAdapter.java
+++ b/telecomm/java/android/telecom/ConnectionServiceAdapter.java
@@ -520,11 +520,14 @@
* @param callId The unique ID of the call.
* @param audioRoute The new audio route (see {@code CallAudioState#ROUTE_*}).
*/
- void setAudioRoute(String callId, int audioRoute) {
- Log.v(this, "setAudioRoute: %s %s", callId, CallAudioState.audioRouteToString(audioRoute));
+ void setAudioRoute(String callId, int audioRoute, String bluetoothAddress) {
+ Log.v(this, "setAudioRoute: %s %s %s", callId,
+ CallAudioState.audioRouteToString(audioRoute),
+ bluetoothAddress);
for (IConnectionServiceAdapter adapter : mAdapters) {
try {
- adapter.setAudioRoute(callId, audioRoute, Log.getExternalSession());
+ adapter.setAudioRoute(callId, audioRoute,
+ bluetoothAddress, Log.getExternalSession());
} catch (RemoteException ignored) {
}
}
diff --git a/telecomm/java/android/telecom/ConnectionServiceAdapterServant.java b/telecomm/java/android/telecom/ConnectionServiceAdapterServant.java
index b1617f4..3fbdeb1 100644
--- a/telecomm/java/android/telecom/ConnectionServiceAdapterServant.java
+++ b/telecomm/java/android/telecom/ConnectionServiceAdapterServant.java
@@ -298,8 +298,8 @@
case MSG_SET_AUDIO_ROUTE: {
SomeArgs args = (SomeArgs) msg.obj;
try {
- mDelegate.setAudioRoute((String) args.arg1, args.argi1,
- (Session.Info) args.arg2);
+ mDelegate.setAudioRoute((String) args.arg1, args.argi1, (String) args.arg2,
+ (Session.Info) args.arg3);
} finally {
args.recycle();
}
@@ -548,12 +548,12 @@
@Override
public final void setAudioRoute(String connectionId, int audioRoute,
- Session.Info sessionInfo) {
-
+ String bluetoothAddress, Session.Info sessionInfo) {
SomeArgs args = SomeArgs.obtain();
args.arg1 = connectionId;
args.argi1 = audioRoute;
- args.arg2 = sessionInfo;
+ args.arg2 = bluetoothAddress;
+ args.arg3 = sessionInfo;
mHandler.obtainMessage(MSG_SET_AUDIO_ROUTE, args).sendToTarget();
}
diff --git a/telecomm/java/android/telecom/InCallAdapter.java b/telecomm/java/android/telecom/InCallAdapter.java
index 9559a28..9bf0467 100644
--- a/telecomm/java/android/telecom/InCallAdapter.java
+++ b/telecomm/java/android/telecom/InCallAdapter.java
@@ -16,6 +16,7 @@
package android.telecom;
+import android.bluetooth.BluetoothDevice;
import android.os.Bundle;
import android.os.RemoteException;
@@ -128,7 +129,22 @@
*/
public void setAudioRoute(int route) {
try {
- mAdapter.setAudioRoute(route);
+ mAdapter.setAudioRoute(route, null);
+ } catch (RemoteException e) {
+ }
+ }
+
+ /**
+ * Request audio routing to a specific bluetooth device. Calling this method may result in
+ * the device routing audio to a different bluetooth device than the one specified. A list of
+ * available devices can be obtained via {@link CallAudioState#getSupportedBluetoothDevices()}
+ *
+ * @param bluetoothAddress The address of the bluetooth device to connect to, as returned by
+ * {@link BluetoothDevice#getAddress()}, or {@code null} if no device is preferred.
+ */
+ public void requestBluetoothAudio(String bluetoothAddress) {
+ try {
+ mAdapter.setAudioRoute(CallAudioState.ROUTE_BLUETOOTH, bluetoothAddress);
} catch (RemoteException e) {
}
}
diff --git a/telecomm/java/android/telecom/InCallService.java b/telecomm/java/android/telecom/InCallService.java
index e384d46..d558bba 100644
--- a/telecomm/java/android/telecom/InCallService.java
+++ b/telecomm/java/android/telecom/InCallService.java
@@ -16,9 +16,11 @@
package android.telecom;
+import android.annotation.NonNull;
import android.annotation.SdkConstant;
import android.annotation.SystemApi;
import android.app.Service;
+import android.bluetooth.BluetoothDevice;
import android.content.Intent;
import android.hardware.camera2.CameraManager;
import android.net.Uri;
@@ -377,6 +379,22 @@
}
/**
+ * Request audio routing to a specific bluetooth device. Calling this method may result in
+ * the device routing audio to a different bluetooth device than the one specified if the
+ * bluetooth stack is unable to route audio to the requested device.
+ * A list of available devices can be obtained via
+ * {@link CallAudioState#getSupportedBluetoothDevices()}
+ *
+ * @param bluetoothAddress The address of the bluetooth device to connect to, as returned by
+ * {@link BluetoothDevice#getAddress()}.
+ */
+ public final void requestBluetoothAudio(@NonNull String bluetoothAddress) {
+ if (mPhone != null) {
+ mPhone.requestBluetoothAudio(bluetoothAddress);
+ }
+ }
+
+ /**
* Invoked when the {@code Phone} has been created. This is a signal to the in-call experience
* to start displaying in-call information to the user. Each instance of {@code InCallService}
* will have only one {@code Phone}, and this method will be called exactly once in the lifetime
diff --git a/telecomm/java/android/telecom/Phone.java b/telecomm/java/android/telecom/Phone.java
index 066f6c2..421b1a4 100644
--- a/telecomm/java/android/telecom/Phone.java
+++ b/telecomm/java/android/telecom/Phone.java
@@ -17,7 +17,9 @@
package android.telecom;
import android.annotation.SystemApi;
+import android.bluetooth.BluetoothDevice;
import android.os.Bundle;
+import android.os.RemoteException;
import android.util.ArrayMap;
import java.util.Collections;
@@ -295,6 +297,18 @@
}
/**
+ * Request audio routing to a specific bluetooth device. Calling this method may result in
+ * the device routing audio to a different bluetooth device than the one specified. A list of
+ * available devices can be obtained via {@link CallAudioState#getSupportedBluetoothDevices()}
+ *
+ * @param bluetoothAddress The address of the bluetooth device to connect to, as returned by
+ * {@link BluetoothDevice#getAddress()}, or {@code null} if no device is preferred.
+ */
+ public void requestBluetoothAudio(String bluetoothAddress) {
+ mInCallAdapter.requestBluetoothAudio(bluetoothAddress);
+ }
+
+ /**
* Turns the proximity sensor on. When this request is made, the proximity sensor will
* become active, and the touch screen and display will be turned off when the user's face
* is detected to be in close proximity to the screen. This operation is a no-op on devices
diff --git a/telecomm/java/android/telecom/RemoteConnectionService.java b/telecomm/java/android/telecom/RemoteConnectionService.java
index 2cc4314..85906ad 100644
--- a/telecomm/java/android/telecom/RemoteConnectionService.java
+++ b/telecomm/java/android/telecom/RemoteConnectionService.java
@@ -398,7 +398,8 @@
}
@Override
- public void setAudioRoute(String callId, int audioRoute, Session.Info sessionInfo) {
+ public void setAudioRoute(String callId, int audioRoute, String bluetoothAddress,
+ Session.Info sessionInfo) {
if (hasConnection(callId)) {
// TODO(3pcalls): handle this for remote connections.
// Likely we don't want to do anything since it doesn't make sense for self-managed
diff --git a/telecomm/java/com/android/internal/telecom/IConnectionServiceAdapter.aidl b/telecomm/java/com/android/internal/telecom/IConnectionServiceAdapter.aidl
index d20da18..da2015f 100644
--- a/telecomm/java/com/android/internal/telecom/IConnectionServiceAdapter.aidl
+++ b/telecomm/java/com/android/internal/telecom/IConnectionServiceAdapter.aidl
@@ -103,7 +103,8 @@
void removeExtras(String callId, in List<String> keys, in Session.Info sessionInfo);
- void setAudioRoute(String callId, int audioRoute, in Session.Info sessionInfo);
+ void setAudioRoute(String callId, int audioRoute, String bluetoothAddress,
+ in Session.Info sessionInfo);
void onConnectionEvent(String callId, String event, in Bundle extras,
in Session.Info sessionInfo);
diff --git a/telecomm/java/com/android/internal/telecom/IInCallAdapter.aidl b/telecomm/java/com/android/internal/telecom/IInCallAdapter.aidl
index 73fa29a..bce3392 100644
--- a/telecomm/java/com/android/internal/telecom/IInCallAdapter.aidl
+++ b/telecomm/java/com/android/internal/telecom/IInCallAdapter.aidl
@@ -39,7 +39,7 @@
void mute(boolean shouldMute);
- void setAudioRoute(int route);
+ void setAudioRoute(int route, String bluetoothAddress);
void playDtmfTone(String callId, char digit);
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 91b2d52..8c64c7f 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -209,6 +209,12 @@
public static final String KEY_SUPPORT_SWAP_AFTER_MERGE_BOOL = "support_swap_after_merge_bool";
/**
+ * Determine whether user can edit voicemail number in Settings.
+ */
+ public static final String KEY_EDITABLE_VOICEMAIL_NUMBER_SETTING_BOOL =
+ "editable_voicemail_number_setting_bool";
+
+ /**
* Since the default voicemail number is empty, if a SIM card does not have a voicemail number
* available the user cannot use voicemail. This flag allows the user to edit the voicemail
* number in such cases, and is false by default.
@@ -1508,6 +1514,13 @@
"boosted_lte_earfcns_string_array";
/**
+ * Determine whether to use only RSRP for the number of LTE signal bars.
+ * @hide
+ */
+ public static final String KEY_USE_ONLY_RSRP_FOR_LTE_SIGNAL_BAR_BOOL =
+ "use_only_rsrp_for_lte_signal_bar_bool";
+
+ /**
* Key identifying if voice call barring notification is required to be shown to the user.
* @hide
*/
@@ -1600,6 +1613,33 @@
public static final String KEY_FEATURE_ACCESS_CODES_STRING_ARRAY =
"feature_access_codes_string_array";
+ /**
+ * Determines if the carrier wants to identify high definition calls in the call log.
+ * @hide
+ */
+ public static final String KEY_IDENTIFY_HIGH_DEFINITION_CALLS_IN_CALL_LOG_BOOL =
+ "identify_high_definition_calls_in_call_log_bool";
+
+ /**
+ * Flag specifying whether to use the {@link ServiceState} roaming status, which can be
+ * affected by other carrier configs (e.g.
+ * {@link #KEY_GSM_NONROAMING_NETWORKS_STRING_ARRAY}), when setting the SPN display.
+ * <p>
+ * If {@code true}, the SPN display uses {@link ServiceState#getRoaming}.
+ * If {@code false} the SPN display checks if the current MCC/MNC is different from the
+ * SIM card's MCC/MNC.
+ *
+ * @see KEY_GSM_ROAMING_NETWORKS_STRING_ARRAY
+ * @see KEY_GSM_NONROAMING_NETWORKS_STRING_ARRAY
+ * @see KEY_NON_ROAMING_OPERATOR_STRING_ARRAY
+ * @see KEY_ROAMING_OPERATOR_STRING_ARRAY
+ * @see KEY_FORCE_HOME_NETWORK_BOOL
+ *
+ * @hide
+ */
+ public static final String KEY_SPN_DISPLAY_RULE_USE_ROAMING_FROM_SERVICE_STATE_BOOL =
+ "spn_display_rule_use_roaming_from_service_state_bool";
+
/** The default value for every variable. */
private final static PersistableBundle sDefaults;
@@ -1659,6 +1699,7 @@
sDefaults.putBoolean(KEY_SUPPORT_PAUSE_IMS_VIDEO_CALLS_BOOL, false);
sDefaults.putBoolean(KEY_SUPPORT_SWAP_AFTER_MERGE_BOOL, true);
sDefaults.putBoolean(KEY_USE_HFA_FOR_PROVISIONING_BOOL, false);
+ sDefaults.putBoolean(KEY_EDITABLE_VOICEMAIL_NUMBER_SETTING_BOOL, true);
sDefaults.putBoolean(KEY_EDITABLE_VOICEMAIL_NUMBER_BOOL, false);
sDefaults.putBoolean(KEY_USE_OTASP_FOR_PROVISIONING_BOOL, false);
sDefaults.putBoolean(KEY_VOICEMAIL_NOTIFICATION_PERSISTENT_BOOL, false);
@@ -1862,6 +1903,7 @@
null);
sDefaults.putInt(KEY_LTE_EARFCNS_RSRP_BOOST_INT, 0);
sDefaults.putStringArray(KEY_BOOSTED_LTE_EARFCNS_STRING_ARRAY, null);
+ sDefaults.putBoolean(KEY_USE_ONLY_RSRP_FOR_LTE_SIGNAL_BAR_BOOL, false);
sDefaults.putBoolean(KEY_DISABLE_VOICE_BARRING_NOTIFICATION_BOOL, false);
sDefaults.putInt(IMSI_KEY_EXPIRATION_DAYS_TIME_INT, IMSI_ENCRYPTION_DAYS_TIME_DISABLED);
sDefaults.putString(IMSI_KEY_DOWNLOAD_URL_STRING, null);
@@ -1872,6 +1914,8 @@
sDefaults.putBoolean(KEY_SHOW_IMS_REGISTRATION_STATUS_BOOL, false);
sDefaults.putBoolean(KEY_DISABLE_CHARGE_INDICATION_BOOL, false);
sDefaults.putStringArray(KEY_FEATURE_ACCESS_CODES_STRING_ARRAY, null);
+ sDefaults.putBoolean(KEY_IDENTIFY_HIGH_DEFINITION_CALLS_IN_CALL_LOG_BOOL, false);
+ sDefaults.putBoolean(KEY_SPN_DISPLAY_RULE_USE_ROAMING_FROM_SERVICE_STATE_BOOL, false);
}
/**
diff --git a/telephony/java/android/telephony/PhoneStateListener.java b/telephony/java/android/telephony/PhoneStateListener.java
index afff6d5..9ccfa94 100644
--- a/telephony/java/android/telephony/PhoneStateListener.java
+++ b/telephony/java/android/telephony/PhoneStateListener.java
@@ -204,16 +204,6 @@
public static final int LISTEN_VOLTE_STATE = 0x00004000;
/**
- * Listen for OEM hook raw event
- *
- * @see #onOemHookRawEvent
- * @hide
- * @deprecated OEM needs a vendor-extension hal and their apps should use that instead
- */
- @Deprecated
- public static final int LISTEN_OEM_HOOK_RAW_EVENT = 0x00008000;
-
- /**
* Listen for carrier network changes indicated by a carrier app.
*
* @see #onCarrierNetworkRequest
@@ -359,9 +349,6 @@
case LISTEN_DATA_ACTIVATION_STATE:
PhoneStateListener.this.onDataActivationStateChanged((int)msg.obj);
break;
- case LISTEN_OEM_HOOK_RAW_EVENT:
- PhoneStateListener.this.onOemHookRawEvent((byte[])msg.obj);
- break;
case LISTEN_CARRIER_NETWORK_CHANGE:
PhoneStateListener.this.onCarrierNetworkChange((boolean)msg.obj);
break;
@@ -556,16 +543,6 @@
}
/**
- * Callback invoked when OEM hook raw event is received. Requires
- * the READ_PRIVILEGED_PHONE_STATE permission.
- * @param rawData is the byte array of the OEM hook raw data.
- * @hide
- */
- public void onOemHookRawEvent(byte[] rawData) {
- // default implementation empty
- }
-
- /**
* Callback invoked when telephony has received notice from a carrier
* app that a network action that could result in connectivity loss
* has been requested by an app using
@@ -677,10 +654,6 @@
send(LISTEN_DATA_ACTIVATION_STATE, 0, 0, activationState);
}
- public void onOemHookRawEvent(byte[] rawData) {
- send(LISTEN_OEM_HOOK_RAW_EVENT, 0, 0, rawData);
- }
-
public void onCarrierNetworkChange(boolean active) {
send(LISTEN_CARRIER_NETWORK_CHANGE, 0, 0, active);
}
diff --git a/telephony/java/android/telephony/SignalStrength.java b/telephony/java/android/telephony/SignalStrength.java
index c8b4776..de02de7 100644
--- a/telephony/java/android/telephony/SignalStrength.java
+++ b/telephony/java/android/telephony/SignalStrength.java
@@ -68,6 +68,7 @@
private int mTdScdmaRscp;
private boolean isGsm; // This value is set by the ServiceStateTracker onSignalStrengthResult
+ private boolean mUseOnlyRsrpForLteLevel; // Use only RSRP for the number of LTE signal bar.
/**
* Create a new SignalStrength from a intent notifier Bundle
@@ -108,6 +109,7 @@
mLteRsrpBoost = 0;
mTdScdmaRscp = INVALID;
isGsm = true;
+ mUseOnlyRsrpForLteLevel = false;
}
/**
@@ -134,6 +136,7 @@
mLteRsrpBoost = 0;
mTdScdmaRscp = INVALID;
isGsm = gsmFlag;
+ mUseOnlyRsrpForLteLevel = false;
}
/**
@@ -145,10 +148,10 @@
int cdmaDbm, int cdmaEcio,
int evdoDbm, int evdoEcio, int evdoSnr,
int lteSignalStrength, int lteRsrp, int lteRsrq, int lteRssnr, int lteCqi,
- int lteRsrpBoost, int tdScdmaRscp, boolean gsmFlag) {
+ int lteRsrpBoost, int tdScdmaRscp, boolean gsmFlag, boolean lteLevelBaseOnRsrp) {
initialize(gsmSignalStrength, gsmBitErrorRate, cdmaDbm, cdmaEcio,
evdoDbm, evdoEcio, evdoSnr, lteSignalStrength, lteRsrp,
- lteRsrq, lteRssnr, lteCqi, lteRsrpBoost, gsmFlag);
+ lteRsrq, lteRssnr, lteCqi, lteRsrpBoost, gsmFlag, lteLevelBaseOnRsrp);
mTdScdmaRscp = tdScdmaRscp;
}
@@ -164,7 +167,7 @@
int tdScdmaRscp, boolean gsmFlag) {
initialize(gsmSignalStrength, gsmBitErrorRate, cdmaDbm, cdmaEcio,
evdoDbm, evdoEcio, evdoSnr, lteSignalStrength, lteRsrp,
- lteRsrq, lteRssnr, lteCqi, 0, gsmFlag);
+ lteRsrq, lteRssnr, lteCqi, 0, gsmFlag, false);
mTdScdmaRscp = tdScdmaRscp;
}
@@ -180,7 +183,7 @@
boolean gsmFlag) {
initialize(gsmSignalStrength, gsmBitErrorRate, cdmaDbm, cdmaEcio,
evdoDbm, evdoEcio, evdoSnr, lteSignalStrength, lteRsrp,
- lteRsrq, lteRssnr, lteCqi, 0, gsmFlag);
+ lteRsrq, lteRssnr, lteCqi, 0, gsmFlag, false);
}
/**
@@ -194,7 +197,7 @@
boolean gsmFlag) {
initialize(gsmSignalStrength, gsmBitErrorRate, cdmaDbm, cdmaEcio,
evdoDbm, evdoEcio, evdoSnr, 99, INVALID,
- INVALID, INVALID, INVALID, 0, gsmFlag);
+ INVALID, INVALID, INVALID, 0, gsmFlag, false);
}
/**
@@ -228,7 +231,7 @@
boolean gsm) {
initialize(gsmSignalStrength, gsmBitErrorRate, cdmaDbm, cdmaEcio,
evdoDbm, evdoEcio, evdoSnr, 99, INVALID,
- INVALID, INVALID, INVALID, 0, gsm);
+ INVALID, INVALID, INVALID, 0, gsm, false);
}
/**
@@ -248,6 +251,7 @@
* @param lteCqi
* @param lteRsrpBoost
* @param gsm
+ * @param useOnlyRsrpForLteLevel
*
* @hide
*/
@@ -255,7 +259,7 @@
int cdmaDbm, int cdmaEcio,
int evdoDbm, int evdoEcio, int evdoSnr,
int lteSignalStrength, int lteRsrp, int lteRsrq, int lteRssnr, int lteCqi,
- int lteRsrpBoost, boolean gsm) {
+ int lteRsrpBoost, boolean gsm, boolean useOnlyRsrpForLteLevel) {
mGsmSignalStrength = gsmSignalStrength;
mGsmBitErrorRate = gsmBitErrorRate;
mCdmaDbm = cdmaDbm;
@@ -271,6 +275,7 @@
mLteRsrpBoost = lteRsrpBoost;
mTdScdmaRscp = INVALID;
isGsm = gsm;
+ mUseOnlyRsrpForLteLevel = useOnlyRsrpForLteLevel;
if (DBG) log("initialize: " + toString());
}
@@ -293,6 +298,7 @@
mLteRsrpBoost = s.mLteRsrpBoost;
mTdScdmaRscp = s.mTdScdmaRscp;
isGsm = s.isGsm;
+ mUseOnlyRsrpForLteLevel = s.mUseOnlyRsrpForLteLevel;
}
/**
@@ -318,6 +324,7 @@
mLteRsrpBoost = in.readInt();
mTdScdmaRscp = in.readInt();
isGsm = (in.readInt() != 0);
+ mUseOnlyRsrpForLteLevel = (in.readInt() != 0);
}
/**
@@ -366,6 +373,7 @@
out.writeInt(mLteRsrpBoost);
out.writeInt(mTdScdmaRscp);
out.writeInt(isGsm ? 1 : 0);
+ out.writeInt(mUseOnlyRsrpForLteLevel ? 1 : 0);
}
/**
@@ -449,6 +457,17 @@
}
/**
+ * @param useOnlyRsrpForLteLevel true if it uses only RSRP for the number of LTE signal bar,
+ * otherwise false.
+ *
+ * Used by phone to use only RSRP or not for the number of LTE signal bar.
+ * @hide
+ */
+ public void setUseOnlyRsrpForLteLevel(boolean useOnlyRsrpForLteLevel) {
+ mUseOnlyRsrpForLteLevel = useOnlyRsrpForLteLevel;
+ }
+
+ /**
* @param lteRsrpBoost - signal strength offset
*
* Used by phone to set the lte signal strength offset which will be
@@ -835,6 +854,13 @@
}
}
+ if (useOnlyRsrpForLteLevel()) {
+ log("getLTELevel - rsrp = " + rsrpIconLevel);
+ if (rsrpIconLevel != -1) {
+ return rsrpIconLevel;
+ }
+ }
+
/*
* Values are -200 dB to +300 (SNR*10dB) RS_SNR >= 13.0 dB =>4 bars 4.5
* dB <= RS_SNR < 13.0 dB => 3 bars 1.0 dB <= RS_SNR < 4.5 dB => 2 bars
@@ -915,6 +941,15 @@
}
/**
+ * @return true if it uses only RSRP for the number of LTE signal bar, otherwise false.
+ *
+ * @hide
+ */
+ public boolean useOnlyRsrpForLteLevel() {
+ return this.mUseOnlyRsrpForLteLevel;
+ }
+
+ /**
* @return get TD_SCDMA dbm
*
* @hide
@@ -974,7 +1009,8 @@
+ (mEvdoDbm * primeNum) + (mEvdoEcio * primeNum) + (mEvdoSnr * primeNum)
+ (mLteSignalStrength * primeNum) + (mLteRsrp * primeNum)
+ (mLteRsrq * primeNum) + (mLteRssnr * primeNum) + (mLteCqi * primeNum)
- + (mLteRsrpBoost * primeNum) + (mTdScdmaRscp * primeNum) + (isGsm ? 1 : 0));
+ + (mLteRsrpBoost * primeNum) + (mTdScdmaRscp * primeNum) + (isGsm ? 1 : 0)
+ + (mUseOnlyRsrpForLteLevel ? 1 : 0));
}
/**
@@ -1008,7 +1044,8 @@
&& mLteCqi == s.mLteCqi
&& mLteRsrpBoost == s.mLteRsrpBoost
&& mTdScdmaRscp == s.mTdScdmaRscp
- && isGsm == s.isGsm);
+ && isGsm == s.isGsm
+ && mUseOnlyRsrpForLteLevel == s.mUseOnlyRsrpForLteLevel);
}
/**
@@ -1031,7 +1068,9 @@
+ " " + mLteCqi
+ " " + mLteRsrpBoost
+ " " + mTdScdmaRscp
- + " " + (isGsm ? "gsm|lte" : "cdma"));
+ + " " + (isGsm ? "gsm|lte" : "cdma")
+ + " " + (mUseOnlyRsrpForLteLevel ? "use_only_rsrp_for_lte_level" :
+ "use_rsrp_and_rssnr_for_lte_level"));
}
/** Returns the signal strength related to GSM. */
@@ -1086,6 +1125,7 @@
mLteRsrpBoost = m.getInt("lteRsrpBoost");
mTdScdmaRscp = m.getInt("TdScdma");
isGsm = m.getBoolean("isGsm");
+ mUseOnlyRsrpForLteLevel = m.getBoolean("useOnlyRsrpForLteLevel");
}
/**
@@ -1110,6 +1150,7 @@
m.putInt("lteRsrpBoost", mLteRsrpBoost);
m.putInt("TdScdma", mTdScdmaRscp);
m.putBoolean("isGsm", isGsm);
+ m.putBoolean("useOnlyRsrpForLteLevel", mUseOnlyRsrpForLteLevel);
}
/**
diff --git a/telephony/java/android/telephony/Telephony.java b/telephony/java/android/telephony/Telephony.java
index e64e815..66ff6e4 100644
--- a/telephony/java/android/telephony/Telephony.java
+++ b/telephony/java/android/telephony/Telephony.java
@@ -2805,6 +2805,26 @@
* @hide
*/
public static final int CARRIER_DELETED_BUT_PRESENT_IN_XML = 6;
+
+ /**
+ * The owner of the APN.
+ * <p>Type: INTEGER</p>
+ * @hide
+ */
+ public static final String OWNED_BY = "owned_by";
+
+ /**
+ * Possible value for the OWNED_BY field.
+ * APN is owned by DPC.
+ * @hide
+ */
+ public static final int OWNED_BY_DPC = 0;
+ /**
+ * Possible value for the OWNED_BY field.
+ * APN is owned by other sources.
+ * @hide
+ */
+ public static final int OWNED_BY_OTHERS = 1;
}
/**
@@ -3250,4 +3270,69 @@
*/
public static final String IS_USING_CARRIER_AGGREGATION = "is_using_carrier_aggregation";
}
+
+ /**
+ * Contains carrier identification information.
+ * @hide
+ */
+ public static final class CarrierIdentification implements BaseColumns {
+ /**
+ * Numeric operator ID (as String). {@code MCC + MNC}
+ * <P>Type: TEXT </P>
+ */
+ public static final String MCCMNC = "mccmnc";
+
+ /**
+ * Group id level 1 (as String).
+ * <P>Type: TEXT </P>
+ */
+ public static final String GID1 = "gid1";
+
+ /**
+ * Group id level 2 (as String).
+ * <P>Type: TEXT </P>
+ */
+ public static final String GID2 = "gid2";
+
+ /**
+ * Public Land Mobile Network name.
+ * <P>Type: TEXT </P>
+ */
+ public static final String PLMN = "plmn";
+
+ /**
+ * Prefix xpattern of IMSI (International Mobile Subscriber Identity).
+ * <P>Type: TEXT </P>
+ */
+ public static final String IMSI_PREFIX_XPATTERN = "imsi_prefix_xpattern";
+
+ /**
+ * Service Provider Name.
+ * <P>Type: TEXT </P>
+ */
+ public static final String SPN = "spn";
+
+ /**
+ * Prefer APN name.
+ * <P>Type: TEXT </P>
+ */
+ public static final String APN = "apn";
+
+ /**
+ * User facing carrier name.
+ * <P>Type: TEXT </P>
+ */
+ public static final String NAME = "carrier_name";
+
+ /**
+ * A unique carrier id
+ * <P>Type: INTEGER </P>
+ */
+ public static final String CID = "carrier_id";
+
+ /**
+ * The {@code content://} URI for this table.
+ */
+ public static final Uri CONTENT_URI = Uri.parse("content://carrier_identification");
+ }
}
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 450f485..f461910 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -2133,13 +2133,16 @@
* @hide
*/
public String getSimOperatorNumeric() {
- int subId = SubscriptionManager.getDefaultDataSubscriptionId();
+ int subId = mSubId;
if (!SubscriptionManager.isUsableSubIdValue(subId)) {
- subId = SubscriptionManager.getDefaultSmsSubscriptionId();
+ subId = SubscriptionManager.getDefaultDataSubscriptionId();
if (!SubscriptionManager.isUsableSubIdValue(subId)) {
- subId = SubscriptionManager.getDefaultVoiceSubscriptionId();
+ subId = SubscriptionManager.getDefaultSmsSubscriptionId();
if (!SubscriptionManager.isUsableSubIdValue(subId)) {
- subId = SubscriptionManager.getDefaultSubscriptionId();
+ subId = SubscriptionManager.getDefaultVoiceSubscriptionId();
+ if (!SubscriptionManager.isUsableSubIdValue(subId)) {
+ subId = SubscriptionManager.getDefaultSubscriptionId();
+ }
}
}
}
@@ -5631,29 +5634,6 @@
return retVal;
}
- /**
- * Returns the result and response from RIL for oem request
- *
- * @param oemReq the data is sent to ril.
- * @param oemResp the respose data from RIL.
- * @return negative value request was not handled or get error
- * 0 request was handled succesfully, but no response data
- * positive value success, data length of response
- * @hide
- * @deprecated OEM needs a vendor-extension hal and their apps should use that instead
- */
- @Deprecated
- public int invokeOemRilRequestRaw(byte[] oemReq, byte[] oemResp) {
- try {
- ITelephony telephony = getITelephony();
- if (telephony != null)
- return telephony.invokeOemRilRequestRaw(oemReq, oemResp);
- } catch (RemoteException ex) {
- } catch (NullPointerException ex) {
- }
- return -1;
- }
-
/** @hide */
@SystemApi
@RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
diff --git a/telephony/java/android/telephony/ims/ImsServiceProxy.java b/telephony/java/android/telephony/ims/ImsServiceProxy.java
deleted file mode 100644
index 038e295..0000000
--- a/telephony/java/android/telephony/ims/ImsServiceProxy.java
+++ /dev/null
@@ -1,335 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package android.telephony.ims;
-
-import android.app.PendingIntent;
-import android.os.IBinder;
-import android.os.Message;
-import android.os.RemoteException;
-import android.telephony.ims.feature.IRcsFeature;
-import android.telephony.ims.feature.ImsFeature;
-import android.util.Log;
-
-import com.android.ims.ImsCallProfile;
-import com.android.ims.internal.IImsCallSession;
-import com.android.ims.internal.IImsCallSessionListener;
-import com.android.ims.internal.IImsConfig;
-import com.android.ims.internal.IImsEcbm;
-import com.android.ims.internal.IImsMultiEndpoint;
-import com.android.ims.internal.IImsRegistrationListener;
-import com.android.ims.internal.IImsServiceController;
-import com.android.ims.internal.IImsServiceFeatureListener;
-import com.android.ims.internal.IImsUt;
-
-/**
- * A container of the IImsServiceController binder, which implements all of the ImsFeatures that
- * the platform currently supports: MMTel and RCS.
- * @hide
- */
-
-public class ImsServiceProxy extends ImsServiceProxyCompat implements IRcsFeature {
-
- protected String LOG_TAG = "ImsServiceProxy";
- private final int mSupportedFeature;
-
- // Start by assuming the proxy is available for usage.
- private boolean mIsAvailable = true;
- // ImsFeature Status from the ImsService. Cached.
- private Integer mFeatureStatusCached = null;
- private ImsServiceProxy.INotifyStatusChanged mStatusCallback;
- private final Object mLock = new Object();
-
- public interface INotifyStatusChanged {
- void notifyStatusChanged();
- }
-
- private final IImsServiceFeatureListener mListenerBinder =
- new IImsServiceFeatureListener.Stub() {
-
- @Override
- public void imsFeatureCreated(int slotId, int feature) throws RemoteException {
- // The feature has been re-enabled. This may happen when the service crashes.
- synchronized (mLock) {
- if (!mIsAvailable && mSlotId == slotId && feature == mSupportedFeature) {
- Log.i(LOG_TAG, "Feature enabled on slotId: " + slotId + " for feature: " +
- feature);
- mIsAvailable = true;
- }
- }
- }
-
- @Override
- public void imsFeatureRemoved(int slotId, int feature) throws RemoteException {
- synchronized (mLock) {
- if (mIsAvailable && mSlotId == slotId && feature == mSupportedFeature) {
- Log.i(LOG_TAG, "Feature disabled on slotId: " + slotId + " for feature: " +
- feature);
- mIsAvailable = false;
- }
- }
- }
-
- @Override
- public void imsStatusChanged(int slotId, int feature, int status) throws RemoteException {
- synchronized (mLock) {
- Log.i(LOG_TAG, "imsStatusChanged: slot: " + slotId + " feature: " + feature +
- " status: " + status);
- if (mSlotId == slotId && feature == mSupportedFeature) {
- mFeatureStatusCached = status;
- if (mStatusCallback != null) {
- mStatusCallback.notifyStatusChanged();
- }
- }
- }
- }
- };
-
- public ImsServiceProxy(int slotId, IBinder binder, int featureType) {
- super(slotId, binder);
- mSupportedFeature = featureType;
- }
-
- public ImsServiceProxy(int slotId, int featureType) {
- super(slotId, null /*IBinder*/);
- mSupportedFeature = featureType;
- }
-
- public IImsServiceFeatureListener getListener() {
- return mListenerBinder;
- }
-
- public void setBinder(IBinder binder) {
- mBinder = binder;
- }
-
- @Override
- public int startSession(PendingIntent incomingCallIntent, IImsRegistrationListener listener)
- throws RemoteException {
- synchronized (mLock) {
- checkServiceIsReady();
- return getServiceInterface(mBinder).startSession(mSlotId, mSupportedFeature,
- incomingCallIntent, listener);
- }
- }
-
- @Override
- public void endSession(int sessionId) throws RemoteException {
- synchronized (mLock) {
- // Only check to make sure the binder connection still exists. This method should
- // still be able to be called when the state is STATE_NOT_AVAILABLE.
- checkBinderConnection();
- getServiceInterface(mBinder).endSession(mSlotId, mSupportedFeature, sessionId);
- }
- }
-
- @Override
- public boolean isConnected(int callServiceType, int callType)
- throws RemoteException {
- synchronized (mLock) {
- checkServiceIsReady();
- return getServiceInterface(mBinder).isConnected(mSlotId, mSupportedFeature,
- callServiceType, callType);
- }
- }
-
- @Override
- public boolean isOpened() throws RemoteException {
- synchronized (mLock) {
- checkServiceIsReady();
- return getServiceInterface(mBinder).isOpened(mSlotId, mSupportedFeature);
- }
- }
-
- @Override
- public void addRegistrationListener(IImsRegistrationListener listener)
- throws RemoteException {
- synchronized (mLock) {
- checkServiceIsReady();
- getServiceInterface(mBinder).addRegistrationListener(mSlotId, mSupportedFeature,
- listener);
- }
- }
-
- @Override
- public void removeRegistrationListener(IImsRegistrationListener listener)
- throws RemoteException {
- synchronized (mLock) {
- checkServiceIsReady();
- getServiceInterface(mBinder).removeRegistrationListener(mSlotId, mSupportedFeature,
- listener);
- }
- }
-
- @Override
- public ImsCallProfile createCallProfile(int sessionId, int callServiceType, int callType)
- throws RemoteException {
- synchronized (mLock) {
- checkServiceIsReady();
- return getServiceInterface(mBinder).createCallProfile(mSlotId, mSupportedFeature,
- sessionId, callServiceType, callType);
- }
- }
-
- @Override
- public IImsCallSession createCallSession(int sessionId, ImsCallProfile profile,
- IImsCallSessionListener listener) throws RemoteException {
- synchronized (mLock) {
- checkServiceIsReady();
- return getServiceInterface(mBinder).createCallSession(mSlotId, mSupportedFeature,
- sessionId, profile, listener);
- }
- }
-
- @Override
- public IImsCallSession getPendingCallSession(int sessionId, String callId)
- throws RemoteException {
- synchronized (mLock) {
- checkServiceIsReady();
- return getServiceInterface(mBinder).getPendingCallSession(mSlotId, mSupportedFeature,
- sessionId, callId);
- }
- }
-
- @Override
- public IImsUt getUtInterface() throws RemoteException {
- synchronized (mLock) {
- checkServiceIsReady();
- return getServiceInterface(mBinder).getUtInterface(mSlotId, mSupportedFeature);
- }
- }
-
- @Override
- public IImsConfig getConfigInterface() throws RemoteException {
- synchronized (mLock) {
- checkServiceIsReady();
- return getServiceInterface(mBinder).getConfigInterface(mSlotId, mSupportedFeature);
- }
- }
-
- @Override
- public void turnOnIms() throws RemoteException {
- synchronized (mLock) {
- checkServiceIsReady();
- getServiceInterface(mBinder).turnOnIms(mSlotId, mSupportedFeature);
- }
- }
-
- @Override
- public void turnOffIms() throws RemoteException {
- synchronized (mLock) {
- checkServiceIsReady();
- getServiceInterface(mBinder).turnOffIms(mSlotId, mSupportedFeature);
- }
- }
-
- @Override
- public IImsEcbm getEcbmInterface() throws RemoteException {
- synchronized (mLock) {
- checkServiceIsReady();
- return getServiceInterface(mBinder).getEcbmInterface(mSlotId, mSupportedFeature);
- }
- }
-
- @Override
- public void setUiTTYMode(int uiTtyMode, Message onComplete)
- throws RemoteException {
- synchronized (mLock) {
- checkServiceIsReady();
- getServiceInterface(mBinder).setUiTTYMode(mSlotId, mSupportedFeature, uiTtyMode,
- onComplete);
- }
- }
-
- @Override
- public IImsMultiEndpoint getMultiEndpointInterface() throws RemoteException {
- synchronized (mLock) {
- checkServiceIsReady();
- return getServiceInterface(mBinder).getMultiEndpointInterface(mSlotId,
- mSupportedFeature);
- }
- }
-
- @Override
- public int getFeatureStatus() {
- synchronized (mLock) {
- if (isBinderAlive() && mFeatureStatusCached != null) {
- Log.i(LOG_TAG, "getFeatureStatus - returning cached: " + mFeatureStatusCached);
- return mFeatureStatusCached;
- }
- }
- // Don't synchronize on Binder call.
- Integer status = retrieveFeatureStatus();
- synchronized (mLock) {
- if (status == null) {
- return ImsFeature.STATE_NOT_AVAILABLE;
- }
- // Cache only non-null value for feature status.
- mFeatureStatusCached = status;
- }
- Log.i(LOG_TAG, "getFeatureStatus - returning " + status);
- return status;
- }
-
- /**
- * Internal method used to retrieve the feature status from the corresponding ImsService.
- */
- private Integer retrieveFeatureStatus() {
- if (mBinder != null) {
- try {
- return getServiceInterface(mBinder).getFeatureStatus(mSlotId, mSupportedFeature);
- } catch (RemoteException e) {
- // Status check failed, don't update cache
- }
- }
- return null;
- }
-
- /**
- * @param c Callback that will fire when the feature status has changed.
- */
- public void setStatusCallback(INotifyStatusChanged c) {
- mStatusCallback = c;
- }
-
- /**
- * @return Returns true if the ImsService is ready to take commands, false otherwise. If this
- * method returns false, it doesn't mean that the Binder connection is not available (use
- * {@link #isBinderReady()} to check that), but that the ImsService is not accepting commands
- * at this time.
- *
- * For example, for DSDS devices, only one slot can be {@link ImsFeature#STATE_READY} to take
- * commands at a time, so the other slot must stay at {@link ImsFeature#STATE_NOT_AVAILABLE}.
- */
- public boolean isBinderReady() {
- return isBinderAlive() && getFeatureStatus() == ImsFeature.STATE_READY;
- }
-
- @Override
- public boolean isBinderAlive() {
- return mIsAvailable && mBinder != null && mBinder.isBinderAlive();
- }
-
- protected void checkServiceIsReady() throws RemoteException {
- if (!isBinderReady()) {
- throw new RemoteException("ImsServiceProxy is not ready to accept commands.");
- }
- }
-
- private IImsServiceController getServiceInterface(IBinder b) {
- return IImsServiceController.Stub.asInterface(b);
- }
-}
diff --git a/telephony/java/android/telephony/ims/ImsServiceProxyCompat.java b/telephony/java/android/telephony/ims/ImsServiceProxyCompat.java
deleted file mode 100644
index bbd5f02..0000000
--- a/telephony/java/android/telephony/ims/ImsServiceProxyCompat.java
+++ /dev/null
@@ -1,183 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package android.telephony.ims;
-
-import android.app.PendingIntent;
-import android.os.IBinder;
-import android.os.Message;
-import android.os.RemoteException;
-import android.telephony.ims.feature.IMMTelFeature;
-import android.telephony.ims.feature.ImsFeature;
-
-import com.android.ims.ImsCallProfile;
-import com.android.ims.internal.IImsCallSession;
-import com.android.ims.internal.IImsCallSessionListener;
-import com.android.ims.internal.IImsConfig;
-import com.android.ims.internal.IImsEcbm;
-import com.android.ims.internal.IImsMultiEndpoint;
-import com.android.ims.internal.IImsRegistrationListener;
-import com.android.ims.internal.IImsService;
-import com.android.ims.internal.IImsUt;
-
-/**
- * Compatibility class that implements the new ImsService IMMTelFeature interface, but
- * uses the old IImsService interface to support older devices that implement the deprecated
- * opt/net/ims interface.
- * @hide
- */
-
-public class ImsServiceProxyCompat implements IMMTelFeature {
-
- private static final int SERVICE_ID = ImsFeature.MMTEL;
-
- protected final int mSlotId;
- protected IBinder mBinder;
-
- public ImsServiceProxyCompat(int slotId, IBinder binder) {
- mSlotId = slotId;
- mBinder = binder;
- }
-
- @Override
- public int startSession(PendingIntent incomingCallIntent, IImsRegistrationListener listener)
- throws RemoteException {
- checkBinderConnection();
- return getServiceInterface(mBinder).open(mSlotId, ImsFeature.MMTEL, incomingCallIntent,
- listener);
- }
-
- @Override
- public void endSession(int sessionId) throws RemoteException {
- checkBinderConnection();
- getServiceInterface(mBinder).close(sessionId);
- }
-
- @Override
- public boolean isConnected(int callServiceType, int callType)
- throws RemoteException {
- checkBinderConnection();
- return getServiceInterface(mBinder).isConnected(SERVICE_ID, callServiceType, callType);
- }
-
- @Override
- public boolean isOpened() throws RemoteException {
- checkBinderConnection();
- return getServiceInterface(mBinder).isOpened(SERVICE_ID);
- }
-
- @Override
- public void addRegistrationListener(IImsRegistrationListener listener)
- throws RemoteException {
- checkBinderConnection();
- getServiceInterface(mBinder).addRegistrationListener(mSlotId, ImsFeature.MMTEL, listener);
- }
-
- @Override
- public void removeRegistrationListener(IImsRegistrationListener listener)
- throws RemoteException {
- // Not Implemented in old ImsService. If the registration listener becomes invalid, the
- // ImsService will remove.
- }
-
- @Override
- public ImsCallProfile createCallProfile(int sessionId, int callServiceType, int callType)
- throws RemoteException {
- checkBinderConnection();
- return getServiceInterface(mBinder).createCallProfile(sessionId, callServiceType, callType);
- }
-
- @Override
- public IImsCallSession createCallSession(int sessionId, ImsCallProfile profile,
- IImsCallSessionListener listener) throws RemoteException {
- checkBinderConnection();
- return getServiceInterface(mBinder).createCallSession(sessionId, profile, listener);
- }
-
- @Override
- public IImsCallSession getPendingCallSession(int sessionId, String callId)
- throws RemoteException {
- checkBinderConnection();
- return getServiceInterface(mBinder).getPendingCallSession(sessionId, callId);
- }
-
- @Override
- public IImsUt getUtInterface() throws RemoteException {
- checkBinderConnection();
- return getServiceInterface(mBinder).getUtInterface(SERVICE_ID);
- }
-
- @Override
- public IImsConfig getConfigInterface() throws RemoteException {
- checkBinderConnection();
- return getServiceInterface(mBinder).getConfigInterface(mSlotId);
- }
-
- @Override
- public void turnOnIms() throws RemoteException {
- checkBinderConnection();
- getServiceInterface(mBinder).turnOnIms(mSlotId);
- }
-
- @Override
- public void turnOffIms() throws RemoteException {
- checkBinderConnection();
- getServiceInterface(mBinder).turnOffIms(mSlotId);
- }
-
- @Override
- public IImsEcbm getEcbmInterface() throws RemoteException {
- checkBinderConnection();
- return getServiceInterface(mBinder).getEcbmInterface(SERVICE_ID);
- }
-
- @Override
- public void setUiTTYMode(int uiTtyMode, Message onComplete)
- throws RemoteException {
- checkBinderConnection();
- getServiceInterface(mBinder).setUiTTYMode(SERVICE_ID, uiTtyMode, onComplete);
- }
-
- @Override
- public IImsMultiEndpoint getMultiEndpointInterface() throws RemoteException {
- checkBinderConnection();
- return getServiceInterface(mBinder).getMultiEndpointInterface(SERVICE_ID);
- }
-
- /**
- * Base implementation, always returns READY for compatibility with old ImsService.
- */
- public int getFeatureStatus() {
- return ImsFeature.STATE_READY;
- }
-
- /**
- * @return false if the binder connection is no longer alive.
- */
- public boolean isBinderAlive() {
- return mBinder != null && mBinder.isBinderAlive();
- }
-
- private IImsService getServiceInterface(IBinder b) {
- return IImsService.Stub.asInterface(b);
- }
-
- protected void checkBinderConnection() throws RemoteException {
- if (!isBinderAlive()) {
- throw new RemoteException("ImsServiceProxy is not available for that feature.");
- }
- }
-}
diff --git a/telephony/java/android/telephony/ims/feature/IMMTelFeature.java b/telephony/java/android/telephony/ims/feature/IMMTelFeature.java
deleted file mode 100644
index d65e27e..0000000
--- a/telephony/java/android/telephony/ims/feature/IMMTelFeature.java
+++ /dev/null
@@ -1,187 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package android.telephony.ims.feature;
-
-import android.app.PendingIntent;
-import android.os.Message;
-import android.os.RemoteException;
-
-import com.android.ims.ImsCallProfile;
-import com.android.ims.internal.IImsCallSession;
-import com.android.ims.internal.IImsCallSessionListener;
-import com.android.ims.internal.IImsConfig;
-import com.android.ims.internal.IImsEcbm;
-import com.android.ims.internal.IImsMultiEndpoint;
-import com.android.ims.internal.IImsRegistrationListener;
-import com.android.ims.internal.IImsUt;
-
-/**
- * MMTel interface for an ImsService. When updating this interface, ensure that base implementations
- * of your changes are also present in MMTelFeature for compatibility with older versions of the
- * MMTel feature.
- * @hide
- */
-
-public interface IMMTelFeature {
-
- /**
- * Notifies the MMTel feature that you would like to start a session. This should always be
- * done before making/receiving IMS calls. The IMS service will register the device to the
- * operator's network with the credentials (from ISIM) periodically in order to receive calls
- * from the operator's network. When the IMS service receives a new call, it will send out an
- * intent with the provided action string. The intent contains a call ID extra
- * {@link IImsCallSession#getCallId} and it can be used to take a call.
- *
- * @param incomingCallIntent When an incoming call is received, the IMS service will call
- * {@link PendingIntent#send} to send back the intent to the caller with
- * {@link #INCOMING_CALL_RESULT_CODE} as the result code and the intent to fill in the call ID;
- * It cannot be null.
- * @param listener To listen to IMS registration events; It cannot be null
- * @return an integer (greater than 0) representing the session id associated with the session
- * that has been started.
- */
- int startSession(PendingIntent incomingCallIntent, IImsRegistrationListener listener)
- throws RemoteException;
-
- /**
- * End a previously started session using the associated sessionId.
- * @param sessionId an integer (greater than 0) representing the ongoing session. See
- * {@link #startSession}.
- */
- void endSession(int sessionId) throws RemoteException;
-
- /**
- * Checks if the IMS service has successfully registered to the IMS network with the specified
- * service & call type.
- *
- * @param callServiceType a service type that is specified in {@link ImsCallProfile}
- * {@link ImsCallProfile#SERVICE_TYPE_NORMAL}
- * {@link ImsCallProfile#SERVICE_TYPE_EMERGENCY}
- * @param callType a call type that is specified in {@link ImsCallProfile}
- * {@link ImsCallProfile#CALL_TYPE_VOICE_N_VIDEO}
- * {@link ImsCallProfile#CALL_TYPE_VOICE}
- * {@link ImsCallProfile#CALL_TYPE_VT}
- * {@link ImsCallProfile#CALL_TYPE_VS}
- * @return true if the specified service id is connected to the IMS network; false otherwise
- * @throws RemoteException
- */
- boolean isConnected(int callServiceType, int callType) throws RemoteException;
-
- /**
- * Checks if the specified IMS service is opened.
- *
- * @return true if the specified service id is opened; false otherwise
- */
- boolean isOpened() throws RemoteException;
-
- /**
- * Add a new registration listener for the client associated with the session Id.
- * @param listener An implementation of IImsRegistrationListener.
- */
- void addRegistrationListener(IImsRegistrationListener listener)
- throws RemoteException;
-
- /**
- * Remove a previously registered listener using {@link #addRegistrationListener} for the client
- * associated with the session Id.
- * @param listener A previously registered IImsRegistrationListener
- */
- void removeRegistrationListener(IImsRegistrationListener listener)
- throws RemoteException;
-
- /**
- * Creates a {@link ImsCallProfile} from the service capabilities & IMS registration state.
- *
- * @param sessionId a session id which is obtained from {@link #startSession}
- * @param callServiceType a service type that is specified in {@link ImsCallProfile}
- * {@link ImsCallProfile#SERVICE_TYPE_NONE}
- * {@link ImsCallProfile#SERVICE_TYPE_NORMAL}
- * {@link ImsCallProfile#SERVICE_TYPE_EMERGENCY}
- * @param callType a call type that is specified in {@link ImsCallProfile}
- * {@link ImsCallProfile#CALL_TYPE_VOICE}
- * {@link ImsCallProfile#CALL_TYPE_VT}
- * {@link ImsCallProfile#CALL_TYPE_VT_TX}
- * {@link ImsCallProfile#CALL_TYPE_VT_RX}
- * {@link ImsCallProfile#CALL_TYPE_VT_NODIR}
- * {@link ImsCallProfile#CALL_TYPE_VS}
- * {@link ImsCallProfile#CALL_TYPE_VS_TX}
- * {@link ImsCallProfile#CALL_TYPE_VS_RX}
- * @return a {@link ImsCallProfile} object
- */
- ImsCallProfile createCallProfile(int sessionId, int callServiceType, int callType)
- throws RemoteException;
-
- /**
- * Creates a {@link ImsCallSession} with the specified call profile.
- * Use other methods, if applicable, instead of interacting with
- * {@link ImsCallSession} directly.
- *
- * @param sessionId a session id which is obtained from {@link #startSession}
- * @param profile a call profile to make the call
- * @param listener An implementation of IImsCallSessionListener.
- */
- IImsCallSession createCallSession(int sessionId, ImsCallProfile profile,
- IImsCallSessionListener listener) throws RemoteException;
-
- /**
- * Retrieves the call session associated with a pending call.
- *
- * @param sessionId a session id which is obtained from {@link #startSession}
- * @param callId a call id to make the call
- */
- IImsCallSession getPendingCallSession(int sessionId, String callId) throws RemoteException;
-
- /**
- * @return The Ut interface for the supplementary service configuration.
- */
- IImsUt getUtInterface() throws RemoteException;
-
- /**
- * @return The config interface for IMS Configuration
- */
- IImsConfig getConfigInterface() throws RemoteException;
-
- /**
- * Signal the MMTelFeature to turn on IMS when it has been turned off using {@link #turnOffIms}
- * @param sessionId a session id which is obtained from {@link #startSession}
- */
- void turnOnIms() throws RemoteException;
-
- /**
- * Signal the MMTelFeature to turn off IMS when it has been turned on using {@link #turnOnIms}
- * @param sessionId a session id which is obtained from {@link #startSession}
- */
- void turnOffIms() throws RemoteException;
-
- /**
- * @return The Emergency call-back mode interface for emergency VoLTE calls that support it.
- */
- IImsEcbm getEcbmInterface() throws RemoteException;
-
- /**
- * Sets the current UI TTY mode for the MMTelFeature.
- * @param uiTtyMode An integer containing the new UI TTY Mode.
- * @param onComplete A {@link Message} to be used when the mode has been set.
- * @throws RemoteException
- */
- void setUiTTYMode(int uiTtyMode, Message onComplete) throws RemoteException;
-
- /**
- * @return MultiEndpoint interface for DEP notifications
- */
- IImsMultiEndpoint getMultiEndpointInterface() throws RemoteException;
-}
diff --git a/telephony/java/android/telephony/ims/feature/IRcsFeature.java b/telephony/java/android/telephony/ims/feature/IRcsFeature.java
deleted file mode 100644
index e28e1b3..0000000
--- a/telephony/java/android/telephony/ims/feature/IRcsFeature.java
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package android.telephony.ims.feature;
-
-/**
- * Feature interface that provides access to RCS APIs. Currently empty until RCS support is added
- * in the framework.
- * @hide
- */
-
-public interface IRcsFeature {
-}
diff --git a/telephony/java/android/telephony/ims/feature/MMTelFeature.java b/telephony/java/android/telephony/ims/feature/MMTelFeature.java
index a71f0bf..758c379 100644
--- a/telephony/java/android/telephony/ims/feature/MMTelFeature.java
+++ b/telephony/java/android/telephony/ims/feature/MMTelFeature.java
@@ -32,90 +32,183 @@
import java.util.List;
/**
- * Base implementation, which implements all methods in IMMTelFeature. Any class wishing to use
- * MMTelFeature should extend this class and implement all methods that the service supports.
+ * Base implementation for MMTel.
+ * Any class wishing to use MMTelFeature should extend this class and implement all methods that the
+ * service supports.
*
* @hide
*/
-public class MMTelFeature extends ImsFeature implements IMMTelFeature {
+public class MMTelFeature extends ImsFeature {
- @Override
+ /**
+ * Notifies the MMTel feature that you would like to start a session. This should always be
+ * done before making/receiving IMS calls. The IMS service will register the device to the
+ * operator's network with the credentials (from ISIM) periodically in order to receive calls
+ * from the operator's network. When the IMS service receives a new call, it will send out an
+ * intent with the provided action string. The intent contains a call ID extra
+ * {@link IImsCallSession#getCallId} and it can be used to take a call.
+ *
+ * @param incomingCallIntent When an incoming call is received, the IMS service will call
+ * {@link PendingIntent#send} to send back the intent to the caller with
+ * ImsManager#INCOMING_CALL_RESULT_CODE as the result code and the intent to fill in the call
+ * ID; It cannot be null.
+ * @param listener To listen to IMS registration events; It cannot be null
+ * @return an integer (greater than 0) representing the session id associated with the session
+ * that has been started.
+ */
public int startSession(PendingIntent incomingCallIntent, IImsRegistrationListener listener) {
return 0;
}
- @Override
+ /**
+ * End a previously started session using the associated sessionId.
+ * @param sessionId an integer (greater than 0) representing the ongoing session. See
+ * {@link #startSession}.
+ */
public void endSession(int sessionId) {
}
- @Override
+ /**
+ * Checks if the IMS service has successfully registered to the IMS network with the specified
+ * service & call type.
+ *
+ * @param callSessionType a service type that is specified in {@link ImsCallProfile}
+ * {@link ImsCallProfile#SERVICE_TYPE_NORMAL}
+ * {@link ImsCallProfile#SERVICE_TYPE_EMERGENCY}
+ * @param callType a call type that is specified in {@link ImsCallProfile}
+ * {@link ImsCallProfile#CALL_TYPE_VOICE_N_VIDEO}
+ * {@link ImsCallProfile#CALL_TYPE_VOICE}
+ * {@link ImsCallProfile#CALL_TYPE_VT}
+ * {@link ImsCallProfile#CALL_TYPE_VS}
+ * @return true if the specified service id is connected to the IMS network; false otherwise
+ */
public boolean isConnected(int callSessionType, int callType) {
return false;
}
- @Override
+ /**
+ * Checks if the specified IMS service is opened.
+ *
+ * @return true if the specified service id is opened; false otherwise
+ */
public boolean isOpened() {
return false;
}
- @Override
+ /**
+ * Add a new registration listener for the client associated with the session Id.
+ * @param listener An implementation of IImsRegistrationListener.
+ */
public void addRegistrationListener(IImsRegistrationListener listener) {
}
- @Override
+ /**
+ * Remove a previously registered listener using {@link #addRegistrationListener} for the client
+ * associated with the session Id.
+ * @param listener A previously registered IImsRegistrationListener
+ */
public void removeRegistrationListener(IImsRegistrationListener listener) {
}
- @Override
+ /**
+ * Creates a {@link ImsCallProfile} from the service capabilities & IMS registration state.
+ *
+ * @param sessionId a session id which is obtained from {@link #startSession}
+ * @param callSessionType a service type that is specified in {@link ImsCallProfile}
+ * {@link ImsCallProfile#SERVICE_TYPE_NONE}
+ * {@link ImsCallProfile#SERVICE_TYPE_NORMAL}
+ * {@link ImsCallProfile#SERVICE_TYPE_EMERGENCY}
+ * @param callType a call type that is specified in {@link ImsCallProfile}
+ * {@link ImsCallProfile#CALL_TYPE_VOICE}
+ * {@link ImsCallProfile#CALL_TYPE_VT}
+ * {@link ImsCallProfile#CALL_TYPE_VT_TX}
+ * {@link ImsCallProfile#CALL_TYPE_VT_RX}
+ * {@link ImsCallProfile#CALL_TYPE_VT_NODIR}
+ * {@link ImsCallProfile#CALL_TYPE_VS}
+ * {@link ImsCallProfile#CALL_TYPE_VS_TX}
+ * {@link ImsCallProfile#CALL_TYPE_VS_RX}
+ * @return a {@link ImsCallProfile} object
+ */
public ImsCallProfile createCallProfile(int sessionId, int callSessionType, int callType) {
return null;
}
- @Override
+ /**
+ * Creates a {@link ImsCallSession} with the specified call profile.
+ * Use other methods, if applicable, instead of interacting with
+ * {@link ImsCallSession} directly.
+ *
+ * @param sessionId a session id which is obtained from {@link #startSession}
+ * @param profile a call profile to make the call
+ * @param listener An implementation of IImsCallSessionListener.
+ */
public IImsCallSession createCallSession(int sessionId, ImsCallProfile profile,
IImsCallSessionListener listener) {
return null;
}
- @Override
+ /**
+ * Retrieves the call session associated with a pending call.
+ *
+ * @param sessionId a session id which is obtained from {@link #startSession}
+ * @param callId a call id to make the call
+ */
public IImsCallSession getPendingCallSession(int sessionId, String callId) {
return null;
}
- @Override
+ /**
+ * @return The Ut interface for the supplementary service configuration.
+ */
public IImsUt getUtInterface() {
return null;
}
- @Override
+ /**
+ * @return The config interface for IMS Configuration
+ */
public IImsConfig getConfigInterface() {
return null;
}
- @Override
+ /**
+ * Signal the MMTelFeature to turn on IMS when it has been turned off using {@link #turnOffIms}
+ */
public void turnOnIms() {
}
- @Override
+ /**
+ * Signal the MMTelFeature to turn off IMS when it has been turned on using {@link #turnOnIms}
+ */
public void turnOffIms() {
}
- @Override
+ /**
+ * @return The Emergency call-back mode interface for emergency VoLTE calls that support it.
+ */
public IImsEcbm getEcbmInterface() {
return null;
}
- @Override
+ /**
+ * Sets the current UI TTY mode for the MMTelFeature.
+ * @param uiTtyMode An integer containing the new UI TTY Mode.
+ * @param onComplete A {@link Message} to be used when the mode has been set.
+ */
public void setUiTTYMode(int uiTtyMode, Message onComplete) {
}
- @Override
+ /**
+ * @return MultiEndpoint interface for DEP notifications
+ */
public IImsMultiEndpoint getMultiEndpointInterface() {
return null;
}
- @Override
+ /**
+ * {@inheritDoc}
+ */
public void onFeatureRemoved() {
}
diff --git a/telephony/java/android/telephony/ims/feature/RcsFeature.java b/telephony/java/android/telephony/ims/feature/RcsFeature.java
index 9cddc1b..332cca3 100644
--- a/telephony/java/android/telephony/ims/feature/RcsFeature.java
+++ b/telephony/java/android/telephony/ims/feature/RcsFeature.java
@@ -18,11 +18,11 @@
/**
* Base implementation of the RcsFeature APIs. Any ImsService wishing to support RCS should extend
- * this class and provide implementations of the IRcsFeature methods that they support.
+ * this class and provide implementations of the RcsFeature methods that they support.
* @hide
*/
-public class RcsFeature extends ImsFeature implements IRcsFeature {
+public class RcsFeature extends ImsFeature {
public RcsFeature() {
super();
diff --git a/telephony/java/com/android/ims/internal/IImsServiceController.aidl b/telephony/java/com/android/ims/internal/IImsServiceController.aidl
index bb06d7e..f1e2262 100644
--- a/telephony/java/com/android/ims/internal/IImsServiceController.aidl
+++ b/telephony/java/com/android/ims/internal/IImsServiceController.aidl
@@ -31,7 +31,7 @@
import android.os.Message;
/**
- * See ImsService and IMMTelFeature for more information.
+ * See ImsService and MMTelFeature for more information.
* {@hide}
*/
interface IImsServiceController {
diff --git a/telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl b/telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl
index e9c5461..ac16139 100644
--- a/telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl
+++ b/telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl
@@ -45,7 +45,6 @@
void onVoLteServiceStateChanged(in VoLteServiceState lteState);
void onVoiceActivationStateChanged(int activationState);
void onDataActivationStateChanged(int activationState);
- void onOemHookRawEvent(in byte[] rawData);
void onCarrierNetworkChange(in boolean active);
}
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index 2ac11b5..3cc9bde 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -1000,17 +1000,6 @@
in List<String> cdmaNonRoamingList);
/**
- * Returns the result and response from RIL for oem request
- *
- * @param oemReq the data is sent to ril.
- * @param oemResp the respose data from RIL.
- * @return negative value request was not handled or get error
- * 0 request was handled succesfully, but no response data
- * positive value success, data length of response
- */
- int invokeOemRilRequestRaw(in byte[] oemReq, out byte[] oemResp);
-
- /**
* Check if any mobile Radios need to be shutdown.
*
* @return true is any mobile radio needs to be shutdown
diff --git a/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl b/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl
index 2c2206c..75d8f3f 100644
--- a/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl
@@ -67,7 +67,6 @@
void notifyVoLteServiceStateChanged(in VoLteServiceState lteState);
void notifySimActivationStateChangedForPhoneId(in int phoneId, in int subId,
int activationState, int activationType);
- void notifyOemHookRawEventForSubscriber(in int subId, in byte[] rawData);
void notifySubscriptionInfoChanged();
void notifyCarrierNetworkChange(in boolean active);
}
diff --git a/telephony/java/com/android/internal/telephony/uicc/IccUtils.java b/telephony/java/com/android/internal/telephony/uicc/IccUtils.java
index 62d570c..99a82ad 100644
--- a/telephony/java/com/android/internal/telephony/uicc/IccUtils.java
+++ b/telephony/java/com/android/internal/telephony/uicc/IccUtils.java
@@ -567,4 +567,12 @@
} while (valueIndex < endIndex);
return result;
}
+
+ public static String getDecimalSubstring(String iccId) {
+ int position;
+ for (position = 0; position < iccId.length(); position ++) {
+ if (!Character.isDigit(iccId.charAt(position))) break;
+ }
+ return iccId.substring( 0, position );
+ }
}
diff --git a/tests/net/java/android/net/MacAddressTest.java b/tests/net/java/android/net/MacAddressTest.java
new file mode 100644
index 0000000..fcbb9da
--- /dev/null
+++ b/tests/net/java/android/net/MacAddressTest.java
@@ -0,0 +1,208 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.fail;
+
+import android.net.MacAddress.MacAddressType;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import java.util.Arrays;
+import java.util.Random;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class MacAddressTest {
+
+ static class AddrTypeTestCase {
+ byte[] addr;
+ MacAddressType expected;
+
+ static AddrTypeTestCase of(MacAddressType expected, int... addr) {
+ AddrTypeTestCase t = new AddrTypeTestCase();
+ t.expected = expected;
+ t.addr = toByteArray(addr);
+ return t;
+ }
+ }
+
+ @Test
+ public void testMacAddrTypes() {
+ AddrTypeTestCase[] testcases = {
+ AddrTypeTestCase.of(null),
+ AddrTypeTestCase.of(null, 0),
+ AddrTypeTestCase.of(null, 1, 2, 3, 4, 5),
+ AddrTypeTestCase.of(null, 1, 2, 3, 4, 5, 6, 7),
+ AddrTypeTestCase.of(MacAddressType.UNICAST, 0xa0, 0xb0, 0xc0, 0xd0, 0xe0, 0xf0),
+ AddrTypeTestCase.of(MacAddressType.BROADCAST, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff),
+ AddrTypeTestCase.of(MacAddressType.MULTICAST, 1, 2, 3, 4, 5, 6),
+ AddrTypeTestCase.of(MacAddressType.MULTICAST, 11, 22, 33, 44, 55, 66),
+ AddrTypeTestCase.of(MacAddressType.MULTICAST, 33, 33, 0xaa, 0xbb, 0xcc, 0xdd)
+ };
+
+ for (AddrTypeTestCase t : testcases) {
+ MacAddressType got = MacAddress.macAddressType(t.addr);
+ String msg = String.format("expected type of %s to be %s, but got %s",
+ Arrays.toString(t.addr), t.expected, got);
+ assertEquals(msg, t.expected, got);
+
+ if (got != null) {
+ assertEquals(got, new MacAddress(t.addr).addressType());
+ }
+ }
+ }
+
+ @Test
+ public void testIsMulticastAddress() {
+ MacAddress[] multicastAddresses = {
+ MacAddress.BROADCAST_ADDRESS,
+ new MacAddress("07:00:d3:56:8a:c4"),
+ new MacAddress("33:33:aa:bb:cc:dd"),
+ };
+ MacAddress[] unicastAddresses = {
+ MacAddress.ALL_ZEROS_ADDRESS,
+ new MacAddress("00:01:44:55:66:77"),
+ new MacAddress("08:00:22:33:44:55"),
+ new MacAddress("06:00:00:00:00:00"),
+ };
+
+ for (MacAddress mac : multicastAddresses) {
+ String msg = mac.toString() + " expected to be a multicast address";
+ assertTrue(msg, mac.isMulticastAddress());
+ }
+ for (MacAddress mac : unicastAddresses) {
+ String msg = mac.toString() + " expected not to be a multicast address";
+ assertFalse(msg, mac.isMulticastAddress());
+ }
+ }
+
+ @Test
+ public void testIsLocallyAssignedAddress() {
+ MacAddress[] localAddresses = {
+ new MacAddress("06:00:00:00:00:00"),
+ new MacAddress("07:00:d3:56:8a:c4"),
+ new MacAddress("33:33:aa:bb:cc:dd"),
+ };
+ MacAddress[] universalAddresses = {
+ new MacAddress("00:01:44:55:66:77"),
+ new MacAddress("08:00:22:33:44:55"),
+ };
+
+ for (MacAddress mac : localAddresses) {
+ String msg = mac.toString() + " expected to be a locally assigned address";
+ assertTrue(msg, mac.isLocallyAssigned());
+ }
+ for (MacAddress mac : universalAddresses) {
+ String msg = mac.toString() + " expected not to be globally unique address";
+ assertFalse(msg, mac.isLocallyAssigned());
+ }
+ }
+
+ @Test
+ public void testMacAddressConversions() {
+ final int iterations = 10000;
+ for (int i = 0; i < iterations; i++) {
+ MacAddress mac = MacAddress.getRandomAddress();
+
+ String stringRepr = mac.toString();
+ byte[] bytesRepr = mac.toByteArray();
+
+ assertEquals(mac, new MacAddress(stringRepr));
+ assertEquals(mac, new MacAddress(bytesRepr));
+ }
+ }
+
+ @Test
+ public void testMacAddressRandomGeneration() {
+ final int iterations = 1000;
+ final String expectedAndroidOui = "da:a1:19";
+ for (int i = 0; i < iterations; i++) {
+ MacAddress mac = MacAddress.getRandomAddress();
+ String stringRepr = mac.toString();
+
+ assertTrue(stringRepr + " expected to be a locally assigned address",
+ mac.isLocallyAssigned());
+ assertTrue(stringRepr + " expected to begin with " + expectedAndroidOui,
+ stringRepr.startsWith(expectedAndroidOui));
+ }
+
+ final Random r = new Random();
+ final String anotherOui = "24:5f:78";
+ final String expectedLocalOui = "26:5f:78";
+ final MacAddress base = new MacAddress(anotherOui + ":0:0:0");
+ for (int i = 0; i < iterations; i++) {
+ MacAddress mac = MacAddress.getRandomAddress(base, r);
+ String stringRepr = mac.toString();
+
+ assertTrue(stringRepr + " expected to be a locally assigned address",
+ mac.isLocallyAssigned());
+ assertTrue(stringRepr + " expected to begin with " + expectedLocalOui,
+ stringRepr.startsWith(expectedLocalOui));
+ }
+ }
+
+ @Test
+ public void testConstructorInputValidation() {
+ String[] invalidStringAddresses = {
+ null,
+ "",
+ "abcd",
+ "1:2:3:4:5",
+ "1:2:3:4:5:6:7",
+ "10000:2:3:4:5:6",
+ };
+
+ for (String s : invalidStringAddresses) {
+ try {
+ MacAddress mac = new MacAddress(s);
+ fail("new MacAddress(" + s + ") should have failed, but returned " + mac);
+ } catch (IllegalArgumentException excepted) {
+ }
+ }
+
+ byte[][] invalidBytesAddresses = {
+ null,
+ {},
+ {1,2,3,4,5},
+ {1,2,3,4,5,6,7},
+ };
+
+ for (byte[] b : invalidBytesAddresses) {
+ try {
+ MacAddress mac = new MacAddress(b);
+ fail("new MacAddress(" + Arrays.toString(b)
+ + ") should have failed, but returned " + mac);
+ } catch (IllegalArgumentException excepted) {
+ }
+ }
+ }
+
+ static byte[] toByteArray(int... in) {
+ byte[] out = new byte[in.length];
+ for (int i = 0; i < in.length; i++) {
+ out[i] = (byte) in[i];
+ }
+ return out;
+ }
+}
diff --git a/tests/net/java/android/net/NetworkCapabilitiesTest.java b/tests/net/java/android/net/NetworkCapabilitiesTest.java
index 7346f9f..cd2d098 100644
--- a/tests/net/java/android/net/NetworkCapabilitiesTest.java
+++ b/tests/net/java/android/net/NetworkCapabilitiesTest.java
@@ -16,6 +16,7 @@
package android.net;
+import static android.net.NetworkCapabilities.LINK_BANDWIDTH_UNSPECIFIED;
import static android.net.NetworkCapabilities.NET_CAPABILITY_CBS;
import static android.net.NetworkCapabilities.NET_CAPABILITY_EIMS;
import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
@@ -26,13 +27,12 @@
import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
import static android.net.NetworkCapabilities.UNRESTRICTED_CAPABILITIES;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertTrue;
-
-import android.net.NetworkCapabilities;
import android.support.test.runner.AndroidJUnit4;
import android.test.suitebuilder.annotation.SmallTest;
@@ -159,4 +159,25 @@
assertNotEquals("", nc1.describeImmutableDifferences(nc2));
assertEquals("", nc1.describeImmutableDifferences(nc1));
}
+
+ @Test
+ public void testLinkBandwidthUtils() {
+ assertEquals(LINK_BANDWIDTH_UNSPECIFIED, NetworkCapabilities
+ .minBandwidth(LINK_BANDWIDTH_UNSPECIFIED, LINK_BANDWIDTH_UNSPECIFIED));
+ assertEquals(10, NetworkCapabilities
+ .minBandwidth(LINK_BANDWIDTH_UNSPECIFIED, 10));
+ assertEquals(10, NetworkCapabilities
+ .minBandwidth(10, LINK_BANDWIDTH_UNSPECIFIED));
+ assertEquals(10, NetworkCapabilities
+ .minBandwidth(10, 20));
+
+ assertEquals(LINK_BANDWIDTH_UNSPECIFIED, NetworkCapabilities
+ .maxBandwidth(LINK_BANDWIDTH_UNSPECIFIED, LINK_BANDWIDTH_UNSPECIFIED));
+ assertEquals(10, NetworkCapabilities
+ .maxBandwidth(LINK_BANDWIDTH_UNSPECIFIED, 10));
+ assertEquals(10, NetworkCapabilities
+ .maxBandwidth(10, LINK_BANDWIDTH_UNSPECIFIED));
+ assertEquals(20, NetworkCapabilities
+ .maxBandwidth(10, 20));
+ }
}
diff --git a/tests/net/java/android/net/apf/ApfTest.java b/tests/net/java/android/net/apf/ApfTest.java
index 99a2ad9..725ddb9 100644
--- a/tests/net/java/android/net/apf/ApfTest.java
+++ b/tests/net/java/android/net/apf/ApfTest.java
@@ -29,9 +29,7 @@
import android.net.LinkAddress;
import android.net.LinkProperties;
import android.net.NetworkUtils;
-import android.net.apf.ApfCapabilities;
-import android.net.apf.ApfFilter;
-import android.net.apf.ApfGenerator;
+import android.net.apf.ApfFilter.ApfConfiguration;
import android.net.apf.ApfGenerator.IllegalInstructionException;
import android.net.apf.ApfGenerator.Register;
import android.net.ip.IpManager;
@@ -99,12 +97,24 @@
// least the minimum packet size.
private final static int MIN_PKT_SIZE = 15;
+ private static final ApfCapabilities MOCK_APF_CAPABILITIES =
+ new ApfCapabilities(2, 1700, ARPHRD_ETHER);
+
private final static boolean DROP_MULTICAST = true;
private final static boolean ALLOW_MULTICAST = false;
private final static boolean DROP_802_3_FRAMES = true;
private final static boolean ALLOW_802_3_FRAMES = false;
+ private static ApfConfiguration getDefaultConfig() {
+ ApfFilter.ApfConfiguration config = new ApfConfiguration();
+ config.apfCapabilities = MOCK_APF_CAPABILITIES;
+ config.multicastFilter = ALLOW_MULTICAST;
+ config.ieee802_3Filter = ALLOW_802_3_FRAMES;
+ config.ethTypeBlackList = new int[0];
+ return config;
+ }
+
private static String label(int code) {
switch (code) {
case PASS: return "PASS";
@@ -619,15 +629,13 @@
private static class TestApfFilter extends ApfFilter {
public final static byte[] MOCK_MAC_ADDR = {1,2,3,4,5,6};
- private FileDescriptor mWriteSocket;
+ private FileDescriptor mWriteSocket;
private final long mFixedTimeMs = SystemClock.elapsedRealtime();
- public TestApfFilter(IpManager.Callback ipManagerCallback, boolean multicastFilter,
- boolean ieee802_3Filter, int[] ethTypeBlackList,
+ public TestApfFilter(ApfConfiguration config, IpManager.Callback ipManagerCallback,
IpConnectivityLog log) throws Exception {
- super(new ApfCapabilities(2, 1700, ARPHRD_ETHER), NetworkInterface.getByName("lo"),
- ipManagerCallback, multicastFilter, ieee802_3Filter, ethTypeBlackList, log);
+ super(config, NetworkInterface.getByName("lo"), ipManagerCallback, log);
}
// Pretend an RA packet has been received and show it to ApfFilter.
@@ -755,10 +763,10 @@
LinkAddress link = new LinkAddress(InetAddress.getByAddress(MOCK_IPV4_ADDR), 19);
LinkProperties lp = new LinkProperties();
lp.addLinkAddress(link);
- final int[] ethTypeBlackList = {};
- ApfFilter apfFilter = new TestApfFilter(ipManagerCallback, DROP_MULTICAST,
- ALLOW_802_3_FRAMES, ethTypeBlackList, mLog);
+ ApfConfiguration config = getDefaultConfig();
+ config.multicastFilter = DROP_MULTICAST;
+ TestApfFilter apfFilter = new TestApfFilter(config, ipManagerCallback, mLog);
apfFilter.setLinkProperties(lp);
byte[] program = ipManagerCallback.getApfProgram();
@@ -808,10 +816,9 @@
@Test
public void testApfFilterIPv6() throws Exception {
- final int[] ethTypeBlackList = {};
MockIpManagerCallback ipManagerCallback = new MockIpManagerCallback();
- ApfFilter apfFilter = new TestApfFilter(ipManagerCallback, ALLOW_MULTICAST,
- ALLOW_802_3_FRAMES, ethTypeBlackList, mLog);
+ ApfConfiguration config = getDefaultConfig();
+ TestApfFilter apfFilter = new TestApfFilter(config, ipManagerCallback, mLog);
byte[] program = ipManagerCallback.getApfProgram();
// Verify empty IPv6 packet is passed
@@ -846,15 +853,15 @@
final byte[] broadcastIpv4Addr = {(byte)192,0,2,(byte)255};
final byte[] multicastIpv4Addr = {(byte)224,0,0,1};
final byte[] multicastIpv6Addr = {(byte)0xff,2,0,0,0,0,0,0,0,0,0,0,0,0,0,(byte)0xfb};
- final int[] ethTypeBlackList = {};
MockIpManagerCallback ipManagerCallback = new MockIpManagerCallback();
LinkAddress link = new LinkAddress(InetAddress.getByAddress(unicastIpv4Addr), 24);
LinkProperties lp = new LinkProperties();
lp.addLinkAddress(link);
- ApfFilter apfFilter = new TestApfFilter(ipManagerCallback, ALLOW_MULTICAST,
- DROP_802_3_FRAMES, ethTypeBlackList, mLog);
+ ApfConfiguration config = getDefaultConfig();
+ config.ieee802_3Filter = DROP_802_3_FRAMES;
+ TestApfFilter apfFilter = new TestApfFilter(config, ipManagerCallback, mLog);
apfFilter.setLinkProperties(lp);
byte[] program = ipManagerCallback.getApfProgram();
@@ -916,8 +923,9 @@
// Verify it can be initialized to on
ipManagerCallback.resetApfProgramWait();
apfFilter.shutdown();
- apfFilter = new TestApfFilter(ipManagerCallback, DROP_MULTICAST,
- DROP_802_3_FRAMES, ethTypeBlackList, mLog);
+ config.multicastFilter = DROP_MULTICAST;
+ config.ieee802_3Filter = DROP_802_3_FRAMES;
+ apfFilter = new TestApfFilter(config, ipManagerCallback, mLog);
apfFilter.setLinkProperties(lp);
program = ipManagerCallback.getApfProgram();
assertDrop(program, mcastv4packet.array());
@@ -938,10 +946,9 @@
LinkAddress link = new LinkAddress(InetAddress.getByAddress(MOCK_IPV4_ADDR), 19);
LinkProperties lp = new LinkProperties();
lp.addLinkAddress(link);
- final int[] ethTypeBlackList = {};
- ApfFilter apfFilter = new TestApfFilter(ipManagerCallback, ALLOW_MULTICAST,
- ALLOW_802_3_FRAMES, ethTypeBlackList, mLog);
+ ApfConfiguration config = getDefaultConfig();
+ TestApfFilter apfFilter = new TestApfFilter(config, ipManagerCallback, mLog);
apfFilter.setLinkProperties(lp);
byte[] program = ipManagerCallback.getApfProgram();
@@ -962,8 +969,8 @@
// Now turn on the filter
ipManagerCallback.resetApfProgramWait();
apfFilter.shutdown();
- apfFilter = new TestApfFilter(ipManagerCallback, ALLOW_MULTICAST,
- DROP_802_3_FRAMES, ethTypeBlackList, mLog);
+ config.ieee802_3Filter = DROP_802_3_FRAMES;
+ apfFilter = new TestApfFilter(config, ipManagerCallback, mLog);
apfFilter.setLinkProperties(lp);
program = ipManagerCallback.getApfProgram();
@@ -993,8 +1000,8 @@
final int[] ipv4BlackList = {ETH_P_IP};
final int[] ipv4Ipv6BlackList = {ETH_P_IP, ETH_P_IPV6};
- ApfFilter apfFilter = new TestApfFilter(ipManagerCallback, ALLOW_MULTICAST,
- ALLOW_802_3_FRAMES, emptyBlackList, mLog);
+ ApfConfiguration config = getDefaultConfig();
+ TestApfFilter apfFilter = new TestApfFilter(config, ipManagerCallback, mLog);
apfFilter.setLinkProperties(lp);
byte[] program = ipManagerCallback.getApfProgram();
@@ -1015,8 +1022,8 @@
// Now add IPv4 to the black list
ipManagerCallback.resetApfProgramWait();
apfFilter.shutdown();
- apfFilter = new TestApfFilter(ipManagerCallback, ALLOW_MULTICAST,
- ALLOW_802_3_FRAMES, ipv4BlackList, mLog);
+ config.ethTypeBlackList = ipv4BlackList;
+ apfFilter = new TestApfFilter(config, ipManagerCallback, mLog);
apfFilter.setLinkProperties(lp);
program = ipManagerCallback.getApfProgram();
@@ -1031,8 +1038,8 @@
// Now let us have both IPv4 and IPv6 in the black list
ipManagerCallback.resetApfProgramWait();
apfFilter.shutdown();
- apfFilter = new TestApfFilter(ipManagerCallback, ALLOW_MULTICAST,
- ALLOW_802_3_FRAMES, ipv4Ipv6BlackList, mLog);
+ config.ethTypeBlackList = ipv4Ipv6BlackList;
+ apfFilter = new TestApfFilter(config, ipManagerCallback, mLog);
apfFilter.setLinkProperties(lp);
program = ipManagerCallback.getApfProgram();
@@ -1070,10 +1077,11 @@
@Test
public void testApfFilterArp() throws Exception {
- final int[] ethTypeBlackList = {};
MockIpManagerCallback ipManagerCallback = new MockIpManagerCallback();
- ApfFilter apfFilter = new TestApfFilter(ipManagerCallback, ALLOW_MULTICAST,
- DROP_802_3_FRAMES, ethTypeBlackList, mLog);
+ ApfConfiguration config = getDefaultConfig();
+ config.multicastFilter = DROP_MULTICAST;
+ config.ieee802_3Filter = DROP_802_3_FRAMES;
+ TestApfFilter apfFilter = new TestApfFilter(config, ipManagerCallback, mLog);
// Verify initially ARP request filter is off, and GARP filter is on.
verifyArpFilter(ipManagerCallback.getApfProgram(), PASS);
@@ -1194,9 +1202,10 @@
@Test
public void testApfFilterRa() throws Exception {
MockIpManagerCallback ipManagerCallback = new MockIpManagerCallback();
- final int[] ethTypeBlackList = {};
- TestApfFilter apfFilter = new TestApfFilter(ipManagerCallback, DROP_MULTICAST,
- DROP_802_3_FRAMES, ethTypeBlackList, mLog);
+ ApfConfiguration config = getDefaultConfig();
+ config.multicastFilter = DROP_MULTICAST;
+ config.ieee802_3Filter = DROP_802_3_FRAMES;
+ TestApfFilter apfFilter = new TestApfFilter(config, ipManagerCallback, mLog);
byte[] program = ipManagerCallback.getApfProgram();
final int ROUTER_LIFETIME = 1000;
@@ -1338,10 +1347,11 @@
public void testRaParsing() throws Exception {
final int maxRandomPacketSize = 512;
final Random r = new Random();
- final int[] ethTypeBlackList = {};
MockIpManagerCallback cb = new MockIpManagerCallback();
- TestApfFilter apfFilter = new TestApfFilter(cb, DROP_MULTICAST,
- DROP_802_3_FRAMES, ethTypeBlackList, mLog);
+ ApfConfiguration config = getDefaultConfig();
+ config.multicastFilter = DROP_MULTICAST;
+ config.ieee802_3Filter = DROP_802_3_FRAMES;
+ TestApfFilter apfFilter = new TestApfFilter(config, cb, mLog);
for (int i = 0; i < 1000; i++) {
byte[] packet = new byte[r.nextInt(maxRandomPacketSize + 1)];
r.nextBytes(packet);
@@ -1358,10 +1368,11 @@
public void testRaProcessing() throws Exception {
final int maxRandomPacketSize = 512;
final Random r = new Random();
- final int[] ethTypeBlackList = {};
MockIpManagerCallback cb = new MockIpManagerCallback();
- TestApfFilter apfFilter = new TestApfFilter(cb, DROP_MULTICAST,
- DROP_802_3_FRAMES, ethTypeBlackList, mLog);
+ ApfConfiguration config = getDefaultConfig();
+ config.multicastFilter = DROP_MULTICAST;
+ config.ieee802_3Filter = DROP_802_3_FRAMES;
+ TestApfFilter apfFilter = new TestApfFilter(config, cb, mLog);
for (int i = 0; i < 1000; i++) {
byte[] packet = new byte[r.nextInt(maxRandomPacketSize + 1)];
r.nextBytes(packet);
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index 27a29b6..113cd37 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -23,21 +23,44 @@
import static android.net.ConnectivityManager.TYPE_MOBILE_MMS;
import static android.net.ConnectivityManager.TYPE_NONE;
import static android.net.ConnectivityManager.TYPE_WIFI;
-import static android.net.ConnectivityManager.getNetworkTypeName;
-import static android.net.NetworkCapabilities.*;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_CAPTIVE_PORTAL;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_CBS;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_DUN;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_EIMS;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_FOREGROUND;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_FOTA;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_IA;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_IMS;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_MMS;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VPN;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_RCS;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_SUPL;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_TRUSTED;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_WIFI_P2P;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_XCAP;
+import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
+import static android.net.NetworkCapabilities.TRANSPORT_ETHERNET;
+import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
+import static android.net.NetworkCapabilities.TRANSPORT_WIFI_AWARE;
+
+import static com.android.internal.util.TestUtils.waitForIdleHandler;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
-import static com.android.internal.util.TestUtils.waitForIdleHandler;
-import static org.mockito.Mockito.anyBoolean;
-import static org.mockito.Mockito.anyInt;
-import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.app.NotificationManager;
@@ -45,7 +68,6 @@
import android.content.BroadcastReceiver;
import android.content.ContentResolver;
import android.content.Context;
-import android.content.ContextWrapper;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.res.Resources;
@@ -64,7 +86,6 @@
import android.net.Network;
import android.net.NetworkAgent;
import android.net.NetworkCapabilities;
-import android.net.NetworkConfig;
import android.net.NetworkFactory;
import android.net.NetworkInfo;
import android.net.NetworkInfo.DetailedState;
@@ -79,13 +100,9 @@
import android.os.ConditionVariable;
import android.os.Handler;
import android.os.HandlerThread;
-import android.os.IBinder;
import android.os.INetworkManagementService;
import android.os.Looper;
import android.os.Message;
-import android.os.MessageQueue;
-import android.os.Messenger;
-import android.os.MessageQueue.IdleHandler;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.Process;
@@ -96,10 +113,8 @@
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
import android.test.mock.MockContentResolver;
-import android.text.TextUtils;
import android.util.ArraySet;
import android.util.Log;
-import android.util.LogPrinter;
import com.android.internal.util.WakeupMessage;
import com.android.internal.util.test.BroadcastInterceptingContext;
@@ -109,7 +124,6 @@
import com.android.server.connectivity.MockableSystemProperties;
import com.android.server.connectivity.NetworkAgentInfo;
import com.android.server.connectivity.NetworkMonitor;
-import com.android.server.connectivity.NetworkMonitor.CaptivePortalProbeResult;
import com.android.server.net.NetworkPinner;
import com.android.server.net.NetworkPolicyManagerInternal;
@@ -160,6 +174,7 @@
@Mock IpConnectivityMetrics.Logger mMetricsService;
@Mock DefaultNetworkMetrics mDefaultNetworkMetrics;
+ @Mock INetworkStatsService mStatsService;
// This class exists to test bindProcessToNetwork and getBoundNetworkForProcess. These methods
// do not go through ConnectivityService but talk to netd directly, so they don't automatically
@@ -858,7 +873,7 @@
NetworkPolicyManagerInternal.class, mock(NetworkPolicyManagerInternal.class));
mService = new WrappedConnectivityService(mServiceContext,
mock(INetworkManagementService.class),
- mock(INetworkStatsService.class),
+ mStatsService,
mock(INetworkPolicyManager.class),
mock(IpConnectivityLog.class));
@@ -3440,6 +3455,40 @@
mCm.unregisterNetworkCallback(networkCallback);
}
+ @Test
+ public void testStatsIfacesChanged() throws Exception {
+ mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
+
+ // Simple connection should have updated ifaces
+ mCellNetworkAgent.connect(false);
+ waitForIdle();
+ verify(mStatsService, atLeastOnce()).forceUpdateIfaces();
+ reset(mStatsService);
+
+ // Metered change should update ifaces
+ mCellNetworkAgent.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED);
+ waitForIdle();
+ verify(mStatsService, atLeastOnce()).forceUpdateIfaces();
+ reset(mStatsService);
+
+ mCellNetworkAgent.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED);
+ waitForIdle();
+ verify(mStatsService, atLeastOnce()).forceUpdateIfaces();
+ reset(mStatsService);
+
+ // Captive portal change shouldn't update ifaces
+ mCellNetworkAgent.addCapability(NetworkCapabilities.NET_CAPABILITY_CAPTIVE_PORTAL);
+ waitForIdle();
+ verify(mStatsService, never()).forceUpdateIfaces();
+ reset(mStatsService);
+
+ // Roaming change should update ifaces
+ mCellNetworkAgent.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING);
+ waitForIdle();
+ verify(mStatsService, atLeastOnce()).forceUpdateIfaces();
+ reset(mStatsService);
+ }
+
private void checkDirectlyConnectedRoutes(Object callbackObj,
Collection<LinkAddress> linkAddresses, Collection<RouteInfo> otherRoutes) {
assertTrue(callbackObj instanceof LinkProperties);
diff --git a/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java b/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java
index 9e97d84b..5c031eb 100644
--- a/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java
+++ b/tests/net/java/com/android/server/IpSecServiceParameterizedTest.java
@@ -64,6 +64,13 @@
return Arrays.asList(new Object[][] {{"8.8.4.4"}, {"2601::10"}});
}
+ private static final byte[] AEAD_KEY = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+ 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F,
+ 0x73, 0x61, 0x6C, 0x74
+ };
private static final byte[] CRYPT_KEY = {
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
@@ -87,7 +94,7 @@
private static final IpSecAlgorithm CRYPT_ALGO =
new IpSecAlgorithm(IpSecAlgorithm.CRYPT_AES_CBC, CRYPT_KEY);
private static final IpSecAlgorithm AEAD_ALGO =
- new IpSecAlgorithm(IpSecAlgorithm.AUTH_CRYPT_AES_GCM, CRYPT_KEY, CRYPT_KEY.length * 4);
+ new IpSecAlgorithm(IpSecAlgorithm.AUTH_CRYPT_AES_GCM, AEAD_KEY, 128);
private static final int[] DIRECTIONS =
new int[] {IpSecTransform.DIRECTION_IN, IpSecTransform.DIRECTION_OUT};
@@ -262,7 +269,7 @@
eq(new byte[] {}),
eq(0),
eq(IpSecAlgorithm.AUTH_CRYPT_AES_GCM),
- eq(CRYPT_KEY),
+ eq(AEAD_KEY),
anyInt(),
anyInt(),
anyInt(),
@@ -283,7 +290,7 @@
eq(new byte[] {}),
eq(0),
eq(IpSecAlgorithm.AUTH_CRYPT_AES_GCM),
- eq(CRYPT_KEY),
+ eq(AEAD_KEY),
anyInt(),
anyInt(),
anyInt(),
diff --git a/tests/net/java/com/android/server/connectivity/IpConnectivityEventBuilderTest.java b/tests/net/java/com/android/server/connectivity/IpConnectivityEventBuilderTest.java
index ad6ebf9..a65bb24 100644
--- a/tests/net/java/com/android/server/connectivity/IpConnectivityEventBuilderTest.java
+++ b/tests/net/java/com/android/server/connectivity/IpConnectivityEventBuilderTest.java
@@ -198,37 +198,33 @@
@Test
public void testDefaultNetworkEventSerialization() {
- DefaultNetworkEvent ev = new DefaultNetworkEvent();
+ DefaultNetworkEvent ev = new DefaultNetworkEvent(1001);
ev.netId = 102;
- ev.prevNetId = 101;
- ev.transportTypes = new int[]{1, 2, 3};
- ev.prevIPv4 = true;
- ev.prevIPv6 = true;
+ ev.transports = 2;
+ ev.previousTransports = 4;
+ ev.ipv4 = true;
+ ev.initialScore = 20;
+ ev.finalScore = 60;
+ ev.durationMs = 54;
+ ev.validatedMs = 27;
String want = String.join("\n",
"dropped_events: 0",
"events <",
" if_name: \"\"",
- " link_layer: 0",
+ " link_layer: 4",
" network_id: 102",
" time_ms: 0",
- " transports: 0",
+ " transports: 2",
" default_network_event <",
- " default_network_duration_ms: 0",
- " final_score: 0",
- " initial_score: 0",
- " ip_support: 0",
- " network_id <",
- " network_id: 102",
- " >",
+ " default_network_duration_ms: 54",
+ " final_score: 60",
+ " initial_score: 20",
+ " ip_support: 1",
" no_default_network_duration_ms: 0",
- " previous_network_id <",
- " network_id: 101",
- " >",
- " previous_network_ip_support: 3",
- " transport_types: 1",
- " transport_types: 2",
- " transport_types: 3",
+ " previous_default_network_link_layer: 1",
+ " previous_network_ip_support: 0",
+ " validation_duration_ms: 27",
" >",
">",
"version: 2\n");
diff --git a/tests/net/java/com/android/server/connectivity/IpConnectivityMetricsTest.java b/tests/net/java/com/android/server/connectivity/IpConnectivityMetricsTest.java
index 6c1decc..b48ff8d 100644
--- a/tests/net/java/com/android/server/connectivity/IpConnectivityMetricsTest.java
+++ b/tests/net/java/com/android/server/connectivity/IpConnectivityMetricsTest.java
@@ -188,119 +188,99 @@
{makeNai(102, 50, true, true, cell), makeNai(103, 20, true, false, wifi)},
};
+ long timeMs = mService.mDefaultNetworkMetrics.creationTimeMs;
+ long durationMs = 1001;
for (NetworkAgentInfo[] pair : defaultNetworks) {
- mService.mDefaultNetworkMetrics.logDefaultNetworkEvent(pair[1], pair[0]);
+ timeMs += durationMs;
+ durationMs += durationMs;
+ mService.mDefaultNetworkMetrics.logDefaultNetworkEvent(timeMs, pair[1], pair[0]);
}
String want = String.join("\n",
"dropped_events: 0",
"events <",
" if_name: \"\"",
- " link_layer: 0",
- " network_id: 100",
- " time_ms: 0",
- " transports: 0",
- " default_network_event <",
- " default_network_duration_ms: 0",
- " final_score: 0",
- " initial_score: 0",
- " ip_support: 0",
- " network_id <",
- " network_id: 100",
- " >",
- " no_default_network_duration_ms: 0",
- " previous_network_id <",
- " network_id: 0",
- " >",
- " previous_network_ip_support: 0",
- " transport_types: 0",
- " >",
- ">",
- "events <",
- " if_name: \"\"",
- " link_layer: 0",
- " network_id: 101",
- " time_ms: 0",
- " transports: 0",
- " default_network_event <",
- " default_network_duration_ms: 0",
- " final_score: 0",
- " initial_score: 0",
- " ip_support: 0",
- " network_id <",
- " network_id: 101",
- " >",
- " no_default_network_duration_ms: 0",
- " previous_network_id <",
- " network_id: 100",
- " >",
- " previous_network_ip_support: 3",
- " transport_types: 1",
- " >",
- ">",
- "events <",
- " if_name: \"\"",
- " link_layer: 0",
+ " link_layer: 5",
" network_id: 0",
" time_ms: 0",
" transports: 0",
" default_network_event <",
- " default_network_duration_ms: 0",
+ " default_network_duration_ms: 1001",
" final_score: 0",
" initial_score: 0",
" ip_support: 0",
- " network_id <",
- " network_id: 0",
- " >",
" no_default_network_duration_ms: 0",
- " previous_network_id <",
- " network_id: 101",
- " >",
- " previous_network_ip_support: 1",
+ " previous_default_network_link_layer: 0",
+ " previous_network_ip_support: 0",
+ " validation_duration_ms: 0",
" >",
">",
"events <",
" if_name: \"\"",
- " link_layer: 0",
+ " link_layer: 2",
+ " network_id: 100",
+ " time_ms: 0",
+ " transports: 1",
+ " default_network_event <",
+ " default_network_duration_ms: 2002",
+ " final_score: 50",
+ " initial_score: 10",
+ " ip_support: 3",
+ " no_default_network_duration_ms: 0",
+ " previous_default_network_link_layer: 0",
+ " previous_network_ip_support: 0",
+ " validation_duration_ms: 2002",
+ " >",
+ ">",
+ "events <",
+ " if_name: \"\"",
+ " link_layer: 4",
+ " network_id: 101",
+ " time_ms: 0",
+ " transports: 2",
+ " default_network_event <",
+ " default_network_duration_ms: 4004",
+ " final_score: 60",
+ " initial_score: 20",
+ " ip_support: 1",
+ " no_default_network_duration_ms: 0",
+ " previous_default_network_link_layer: 2",
+ " previous_network_ip_support: 0",
+ " validation_duration_ms: 4004",
+ " >",
+ ">",
+ "events <",
+ " if_name: \"\"",
+ " link_layer: 5",
+ " network_id: 0",
+ " time_ms: 0",
+ " transports: 0",
+ " default_network_event <",
+ " default_network_duration_ms: 8008",
+ " final_score: 0",
+ " initial_score: 0",
+ " ip_support: 0",
+ " no_default_network_duration_ms: 0",
+ " previous_default_network_link_layer: 4",
+ " previous_network_ip_support: 0",
+ " validation_duration_ms: 0",
+ " >",
+ ">",
+ "events <",
+ " if_name: \"\"",
+ " link_layer: 2",
" network_id: 102",
" time_ms: 0",
- " transports: 0",
+ " transports: 1",
" default_network_event <",
- " default_network_duration_ms: 0",
- " final_score: 0",
- " initial_score: 0",
- " ip_support: 0",
- " network_id <",
- " network_id: 102",
- " >",
+ " default_network_duration_ms: 16016",
+ " final_score: 50",
+ " initial_score: 10",
+ " ip_support: 3",
" no_default_network_duration_ms: 0",
- " previous_network_id <",
- " network_id: 0",
- " >",
+ " previous_default_network_link_layer: 4",
" previous_network_ip_support: 0",
- " transport_types: 0",
- " >",
- ">",
- "events <",
- " if_name: \"\"",
- " link_layer: 0",
- " network_id: 103",
- " time_ms: 0",
- " transports: 0",
- " default_network_event <",
- " default_network_duration_ms: 0",
- " final_score: 0",
- " initial_score: 0",
- " ip_support: 0",
- " network_id <",
- " network_id: 103",
- " >",
- " no_default_network_duration_ms: 0",
- " previous_network_id <",
- " network_id: 102",
- " >",
- " previous_network_ip_support: 3",
- " transport_types: 1",
+ " validation_duration_ms: 16016",
" >",
">",
"version: 2\n");
@@ -379,12 +359,13 @@
wakeupEvent("wlan0", 10008);
wakeupEvent("rmnet0", 1000);
+ long timeMs = mService.mDefaultNetworkMetrics.creationTimeMs;
final long cell = BitUtils.packBits(new int[]{NetworkCapabilities.TRANSPORT_CELLULAR});
final long wifi = BitUtils.packBits(new int[]{NetworkCapabilities.TRANSPORT_WIFI});
NetworkAgentInfo cellNai = makeNai(100, 50, false, true, cell);
NetworkAgentInfo wifiNai = makeNai(101, 60, true, false, wifi);
- mService.mDefaultNetworkMetrics.logDefaultNetworkEvent(cellNai, null);
- mService.mDefaultNetworkMetrics.logDefaultNetworkEvent(wifiNai, cellNai);
+ mService.mDefaultNetworkMetrics.logDefaultNetworkEvent(timeMs + 200, cellNai, null);
+ mService.mDefaultNetworkMetrics.logDefaultNetworkEvent(timeMs + 300, wifiNai, cellNai);
String want = String.join("\n",
"dropped_events: 0",
@@ -473,46 +454,36 @@
">",
"events <",
" if_name: \"\"",
- " link_layer: 0",
- " network_id: 100",
+ " link_layer: 5",
+ " network_id: 0",
" time_ms: 0",
" transports: 0",
" default_network_event <",
- " default_network_duration_ms: 0",
+ " default_network_duration_ms: 200",
" final_score: 0",
" initial_score: 0",
" ip_support: 0",
- " network_id <",
- " network_id: 100",
- " >",
" no_default_network_duration_ms: 0",
- " previous_network_id <",
- " network_id: 0",
- " >",
+ " previous_default_network_link_layer: 0",
" previous_network_ip_support: 0",
- " transport_types: 0",
+ " validation_duration_ms: 0",
" >",
">",
"events <",
" if_name: \"\"",
- " link_layer: 0",
- " network_id: 101",
+ " link_layer: 2",
+ " network_id: 100",
" time_ms: 0",
- " transports: 0",
+ " transports: 1",
" default_network_event <",
- " default_network_duration_ms: 0",
- " final_score: 0",
- " initial_score: 0",
- " ip_support: 0",
- " network_id <",
- " network_id: 101",
- " >",
+ " default_network_duration_ms: 100",
+ " final_score: 50",
+ " initial_score: 50",
+ " ip_support: 2",
" no_default_network_duration_ms: 0",
- " previous_network_id <",
- " network_id: 100",
- " >",
- " previous_network_ip_support: 2",
- " transport_types: 1",
+ " previous_default_network_link_layer: 0",
+ " previous_network_ip_support: 0",
+ " validation_duration_ms: 100",
" >",
">",
"events <",
diff --git a/tests/net/java/com/android/server/connectivity/NetdEventListenerServiceTest.java b/tests/net/java/com/android/server/connectivity/NetdEventListenerServiceTest.java
index 6723601..83194d9 100644
--- a/tests/net/java/com/android/server/connectivity/NetdEventListenerServiceTest.java
+++ b/tests/net/java/com/android/server/connectivity/NetdEventListenerServiceTest.java
@@ -82,9 +82,8 @@
public void testWakeupEventLogging() throws Exception {
final int BUFFER_LENGTH = NetdEventListenerService.WAKEUP_EVENT_BUFFER_LENGTH;
- // Assert no events
- String[] events1 = listNetdEvent();
- assertEquals(new String[]{""}, events1);
+ // Baseline without any event
+ String[] baseline = listNetdEvent();
long now = System.currentTimeMillis();
String prefix = "iface:wlan0";
@@ -93,7 +92,7 @@
mNetdEventListenerService.onWakeupEvent(prefix, uid, uid, now);
}
- String[] events2 = listNetdEvent();
+ String[] events2 = remove(listNetdEvent(), baseline);
int expectedLength2 = uids.length + 1; // +1 for the WakeupStats line
assertEquals(expectedLength2, events2.length);
assertContains(events2[0], "WakeupStats");
@@ -111,7 +110,7 @@
mNetdEventListenerService.onWakeupEvent(prefix, uid, uid, ts);
}
- String[] events3 = listNetdEvent();
+ String[] events3 = remove(listNetdEvent(), baseline);
int expectedLength3 = BUFFER_LENGTH + 1; // +1 for the WakeupStats line
assertEquals(expectedLength3, events3.length);
assertContains(events2[0], "WakeupStats");
@@ -126,7 +125,7 @@
uid = 45678;
mNetdEventListenerService.onWakeupEvent(prefix, uid, uid, now);
- String[] events4 = listNetdEvent();
+ String[] events4 = remove(listNetdEvent(), baseline);
String lastEvent = events4[events4.length - 1];
assertContains(lastEvent, "WakeupEvent");
assertContains(lastEvent, "wlan0");
@@ -423,7 +422,7 @@
final PrintWriter pw = new PrintWriter(new FileOutputStream("/dev/null"));
new Thread(() -> {
while (System.currentTimeMillis() < stop) {
- mNetdEventListenerService.dump(pw);
+ mNetdEventListenerService.list(pw);
}
}).start();
}
@@ -461,4 +460,16 @@
static void assertContains(String got, String want) {
assertTrue(got + " did not contain \"" + want + "\"", got.contains(want));
}
+
+ static <T> T[] remove(T[] array, T[] filtered) {
+ List<T> c = Arrays.asList(filtered);
+ int next = 0;
+ for (int i = 0; i < array.length; i++) {
+ if (c.contains(array[i])) {
+ continue;
+ }
+ array[next++] = array[i];
+ }
+ return Arrays.copyOf(array, next);
+ }
}
diff --git a/tests/net/java/com/android/server/connectivity/VpnTest.java b/tests/net/java/com/android/server/connectivity/VpnTest.java
index fe396c3..c29363c 100644
--- a/tests/net/java/com/android/server/connectivity/VpnTest.java
+++ b/tests/net/java/com/android/server/connectivity/VpnTest.java
@@ -20,11 +20,30 @@
import static android.content.pm.UserInfo.FLAG_MANAGED_PROFILE;
import static android.content.pm.UserInfo.FLAG_PRIMARY;
import static android.content.pm.UserInfo.FLAG_RESTRICTED;
+import static android.net.NetworkCapabilities.LINK_BANDWIDTH_UNSPECIFIED;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING;
+import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
+import static android.net.NetworkCapabilities.TRANSPORT_VPN;
+import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
-import static org.mockito.AdditionalMatchers.*;
-import static org.mockito.Mockito.*;
+import static org.mockito.AdditionalMatchers.aryEq;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.atLeastOnce;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.inOrder;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
import android.annotation.UserIdInt;
import android.app.AppOpsManager;
@@ -36,6 +55,9 @@
import android.content.pm.ServiceInfo;
import android.content.pm.UserInfo;
import android.content.res.Resources;
+import android.net.ConnectivityManager;
+import android.net.Network;
+import android.net.NetworkCapabilities;
import android.net.NetworkInfo.DetailedState;
import android.net.UidRange;
import android.net.VpnService;
@@ -45,17 +67,17 @@
import android.os.Looper;
import android.os.UserHandle;
import android.os.UserManager;
-import android.support.test.runner.AndroidJUnit4;
import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
import android.util.ArrayMap;
import android.util.ArraySet;
import com.android.internal.R;
import com.android.internal.net.VpnConfig;
-import org.junit.runner.RunWith;
import org.junit.Before;
import org.junit.Test;
+import org.junit.runner.RunWith;
import org.mockito.Answers;
import org.mockito.InOrder;
import org.mockito.Mock;
@@ -64,6 +86,7 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
+import java.util.HashMap;
import java.util.Map;
import java.util.Set;
@@ -114,6 +137,7 @@
@Mock private AppOpsManager mAppOps;
@Mock private NotificationManager mNotificationManager;
@Mock private Vpn.SystemServices mSystemServices;
+ @Mock private ConnectivityManager mConnectivityManager;
@Before
public void setUp() throws Exception {
@@ -127,6 +151,8 @@
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.getString(R.string.config_customVpnAlwaysOnDisconnectedDialogComponent))
.thenReturn(Resources.getSystem().getString(
R.string.config_customVpnAlwaysOnDisconnectedDialogComponent));
@@ -397,6 +423,66 @@
order.verify(mNotificationManager).cancelAsUser(anyString(), anyInt(), eq(userHandle));
}
+ @Test
+ public void testCapabilities() {
+ final Vpn vpn = createVpn(primaryUser.id);
+ setMockedUsers(primaryUser);
+
+ final Network mobile = new Network(1);
+ final Network wifi = new Network(2);
+
+ final Map<Network, NetworkCapabilities> networks = new HashMap<>();
+ networks.put(mobile, new NetworkCapabilities()
+ .addTransportType(TRANSPORT_CELLULAR)
+ .addCapability(NET_CAPABILITY_INTERNET)
+ .addCapability(NET_CAPABILITY_NOT_METERED)
+ .setLinkDownstreamBandwidthKbps(10));
+ networks.put(wifi, new NetworkCapabilities()
+ .addTransportType(TRANSPORT_WIFI)
+ .addCapability(NET_CAPABILITY_INTERNET)
+ .addCapability(NET_CAPABILITY_NOT_ROAMING)
+ .setLinkUpstreamBandwidthKbps(20));
+ setMockedNetworks(networks);
+
+ final NetworkCapabilities caps = new NetworkCapabilities();
+
+ Vpn.updateCapabilities(mConnectivityManager, new Network[] { }, caps);
+ assertTrue(caps.hasTransport(TRANSPORT_VPN));
+ assertFalse(caps.hasTransport(TRANSPORT_CELLULAR));
+ assertFalse(caps.hasTransport(TRANSPORT_WIFI));
+ assertEquals(LINK_BANDWIDTH_UNSPECIFIED, caps.getLinkDownstreamBandwidthKbps());
+ assertEquals(LINK_BANDWIDTH_UNSPECIFIED, caps.getLinkUpstreamBandwidthKbps());
+ assertFalse(caps.hasCapability(NET_CAPABILITY_NOT_METERED));
+ assertTrue(caps.hasCapability(NET_CAPABILITY_NOT_ROAMING));
+
+ Vpn.updateCapabilities(mConnectivityManager, new Network[] { mobile }, caps);
+ assertTrue(caps.hasTransport(TRANSPORT_VPN));
+ assertTrue(caps.hasTransport(TRANSPORT_CELLULAR));
+ assertFalse(caps.hasTransport(TRANSPORT_WIFI));
+ assertEquals(10, caps.getLinkDownstreamBandwidthKbps());
+ assertEquals(LINK_BANDWIDTH_UNSPECIFIED, caps.getLinkUpstreamBandwidthKbps());
+ assertTrue(caps.hasCapability(NET_CAPABILITY_NOT_METERED));
+ assertFalse(caps.hasCapability(NET_CAPABILITY_NOT_ROAMING));
+
+ Vpn.updateCapabilities(mConnectivityManager, new Network[] { wifi }, caps);
+ assertTrue(caps.hasTransport(TRANSPORT_VPN));
+ assertFalse(caps.hasTransport(TRANSPORT_CELLULAR));
+ assertTrue(caps.hasTransport(TRANSPORT_WIFI));
+ assertEquals(LINK_BANDWIDTH_UNSPECIFIED, caps.getLinkDownstreamBandwidthKbps());
+ assertEquals(20, caps.getLinkUpstreamBandwidthKbps());
+ assertFalse(caps.hasCapability(NET_CAPABILITY_NOT_METERED));
+ assertTrue(caps.hasCapability(NET_CAPABILITY_NOT_ROAMING));
+
+ Vpn.updateCapabilities(mConnectivityManager, new Network[] { mobile, wifi }, caps);
+ assertTrue(caps.hasTransport(TRANSPORT_VPN));
+ assertTrue(caps.hasTransport(TRANSPORT_CELLULAR));
+ assertTrue(caps.hasTransport(TRANSPORT_WIFI));
+ assertEquals(10, caps.getLinkDownstreamBandwidthKbps());
+ assertEquals(20, caps.getLinkUpstreamBandwidthKbps());
+ assertFalse(caps.hasCapability(NET_CAPABILITY_NOT_METERED));
+ assertFalse(caps.hasCapability(NET_CAPABILITY_NOT_ROAMING));
+ }
+
/**
* Mock some methods of vpn object.
*/
@@ -463,4 +549,11 @@
} catch (Exception e) {
}
}
+
+ private void setMockedNetworks(final Map<Network, NetworkCapabilities> networks) {
+ doAnswer(invocation -> {
+ final Network network = (Network) invocation.getArguments()[0];
+ return networks.get(network);
+ }).when(mConnectivityManager).getNetworkCapabilities(any());
+ }
}
diff --git a/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java b/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java
index fa99795..375b418 100644
--- a/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java
+++ b/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java
@@ -1169,9 +1169,8 @@
final LinkProperties prop = new LinkProperties();
prop.setInterfaceName(TEST_IFACE);
final NetworkCapabilities capabilities = new NetworkCapabilities();
- if (!isMetered) {
- capabilities.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED);
- }
+ capabilities.setCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED, !isMetered);
+ capabilities.setCapability(NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING, true);
return new NetworkState(info, prop, capabilities, null, null, TEST_SSID);
}
@@ -1187,6 +1186,8 @@
final LinkProperties prop = new LinkProperties();
prop.setInterfaceName(TEST_IFACE);
final NetworkCapabilities capabilities = new NetworkCapabilities();
+ capabilities.setCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED, false);
+ capabilities.setCapability(NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING, !isRoaming);
return new NetworkState(info, prop, capabilities, null, subscriberId, null);
}
@@ -1196,6 +1197,8 @@
final LinkProperties prop = new LinkProperties();
prop.setInterfaceName(iface);
final NetworkCapabilities capabilities = new NetworkCapabilities();
+ capabilities.setCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED, false);
+ capabilities.setCapability(NetworkCapabilities.NET_CAPABILITY_NOT_ROAMING, true);
return new NetworkState(info, prop, capabilities, null, null, null);
}