Merge "Restricted permission mechanism - framework" into qt-dev
diff --git a/api/current.txt b/api/current.txt
index d24e350..561a20b 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -52359,18 +52359,18 @@
method public void setCheckable(boolean);
method public void setChecked(boolean);
method public void setClassName(CharSequence);
- method @Deprecated public void setClickable(boolean);
+ method public void setClickable(boolean);
method public void setCollectionInfo(android.view.accessibility.AccessibilityNodeInfo.CollectionInfo);
method public void setCollectionItemInfo(android.view.accessibility.AccessibilityNodeInfo.CollectionItemInfo);
method public void setContentDescription(CharSequence);
method public void setContentInvalid(boolean);
- method @Deprecated public void setContextClickable(boolean);
- method @Deprecated public void setDismissable(boolean);
+ method public void setContextClickable(boolean);
+ method public void setDismissable(boolean);
method public void setDrawingOrder(int);
method public void setEditable(boolean);
method public void setEnabled(boolean);
method public void setError(CharSequence);
- method @Deprecated public void setFocusable(boolean);
+ method public void setFocusable(boolean);
method public void setFocused(boolean);
method public void setHeading(boolean);
method public void setHintText(CharSequence);
@@ -52381,7 +52381,7 @@
method public void setLabeledBy(android.view.View);
method public void setLabeledBy(android.view.View, int);
method public void setLiveRegion(int);
- method @Deprecated public void setLongClickable(boolean);
+ method public void setLongClickable(boolean);
method public void setMaxTextLength(int);
method public void setMovementGranularities(int);
method public void setMultiLine(boolean);
@@ -52392,7 +52392,7 @@
method public void setPassword(boolean);
method public void setRangeInfo(android.view.accessibility.AccessibilityNodeInfo.RangeInfo);
method public void setScreenReaderFocusable(boolean);
- method @Deprecated public void setScrollable(boolean);
+ method public void setScrollable(boolean);
method public void setSelected(boolean);
method public void setShowingHintText(boolean);
method public void setSource(android.view.View);
diff --git a/api/system-current.txt b/api/system-current.txt
index eb9f82a..3ee6a49 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -4461,16 +4461,11 @@
package android.net.util {
public final class SocketUtils {
- method public static void addArpEntry(@NonNull java.net.Inet4Address, @NonNull android.net.MacAddress, @NonNull String, @NonNull java.io.FileDescriptor) throws java.io.IOException;
- method public static void attachControlPacketFilter(@NonNull java.io.FileDescriptor, int) throws java.net.SocketException;
- method public static void attachDhcpFilter(@NonNull java.io.FileDescriptor) throws java.net.SocketException;
- method public static void attachRaFilter(@NonNull java.io.FileDescriptor, int) throws java.net.SocketException;
method public static void bindSocketToInterface(@NonNull java.io.FileDescriptor, @NonNull String) throws android.system.ErrnoException;
method public static void closeSocket(@Nullable java.io.FileDescriptor) throws java.io.IOException;
method @NonNull public static java.net.SocketAddress makeNetlinkSocketAddress(int, int);
method @NonNull public static java.net.SocketAddress makePacketSocketAddress(int, int);
method @NonNull public static java.net.SocketAddress makePacketSocketAddress(int, @NonNull byte[]);
- method public static void setSocketTimeValueOption(@NonNull java.io.FileDescriptor, int, int, long) throws android.system.ErrnoException;
}
}
@@ -6108,7 +6103,6 @@
field public static final String LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS = "lock_screen_allow_private_notifications";
field public static final String LOCK_SCREEN_SHOW_NOTIFICATIONS = "lock_screen_show_notifications";
field public static final String ODI_CAPTIONS_ENABLED = "odi_captions_enabled";
- field public static final String ODI_CAPTIONS_OPTED_OUT = "odi_captions_opted_out";
field public static final String THEME_CUSTOMIZATION_OVERLAY_PACKAGES = "theme_customization_overlay_packages";
field public static final String USER_SETUP_COMPLETE = "user_setup_complete";
field public static final int USER_SETUP_PERSONALIZATION_COMPLETE = 10; // 0xa
diff --git a/api/test-current.txt b/api/test-current.txt
index 973e700..b35b90f 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -1552,16 +1552,11 @@
package android.net.util {
public final class SocketUtils {
- method public static void addArpEntry(@NonNull java.net.Inet4Address, @NonNull android.net.MacAddress, @NonNull String, @NonNull java.io.FileDescriptor) throws java.io.IOException;
- method public static void attachControlPacketFilter(@NonNull java.io.FileDescriptor, int) throws java.net.SocketException;
- method public static void attachDhcpFilter(@NonNull java.io.FileDescriptor) throws java.net.SocketException;
- method public static void attachRaFilter(@NonNull java.io.FileDescriptor, int) throws java.net.SocketException;
method public static void bindSocketToInterface(@NonNull java.io.FileDescriptor, @NonNull String) throws android.system.ErrnoException;
method public static void closeSocket(@Nullable java.io.FileDescriptor) throws java.io.IOException;
method @NonNull public static java.net.SocketAddress makeNetlinkSocketAddress(int, int);
method @NonNull public static java.net.SocketAddress makePacketSocketAddress(int, int);
method @NonNull public static java.net.SocketAddress makePacketSocketAddress(int, @NonNull byte[]);
- method public static void setSocketTimeValueOption(@NonNull java.io.FileDescriptor, int, int, long) throws android.system.ErrnoException;
}
}
diff --git a/core/java/android/net/NetworkUtils.java b/core/java/android/net/NetworkUtils.java
index 5188866..db87c97 100644
--- a/core/java/android/net/NetworkUtils.java
+++ b/core/java/android/net/NetworkUtils.java
@@ -29,7 +29,6 @@
import android.util.Pair;
import java.io.FileDescriptor;
-import java.io.IOException;
import java.math.BigInteger;
import java.net.Inet4Address;
import java.net.Inet6Address;
@@ -50,32 +49,6 @@
private static final String TAG = "NetworkUtils";
/**
- * Attaches a socket filter that accepts DHCP packets to the given socket.
- */
- @UnsupportedAppUsage
- public native static void attachDhcpFilter(FileDescriptor fd) throws SocketException;
-
- /**
- * Attaches a socket filter that accepts ICMPv6 router advertisements to the given socket.
- * @param fd the socket's {@link FileDescriptor}.
- * @param packetType the hardware address type, one of ARPHRD_*.
- */
- @UnsupportedAppUsage
- public native static void attachRaFilter(FileDescriptor fd, int packetType) throws SocketException;
-
- /**
- * Attaches a socket filter that accepts L2-L4 signaling traffic required for IP connectivity.
- *
- * This includes: all ARP, ICMPv6 RS/RA/NS/NA messages, and DHCPv4 exchanges.
- *
- * @param fd the socket's {@link FileDescriptor}.
- * @param packetType the hardware address type, one of ARPHRD_*.
- */
- @UnsupportedAppUsage
- public native static void attachControlPacketFilter(FileDescriptor fd, int packetType)
- throws SocketException;
-
- /**
* Attaches a socket filter that drops all of incoming packets.
* @param fd the socket's {@link FileDescriptor}.
*/
@@ -183,18 +156,6 @@
public static native void resNetworkCancel(FileDescriptor fd);
/**
- * Add an entry into the ARP cache.
- */
- public static void addArpEntry(Inet4Address ipv4Addr, MacAddress ethAddr, String ifname,
- FileDescriptor fd) throws IOException {
- addArpEntry(ethAddr.toByteArray(), ipv4Addr.getAddress(), ifname, fd);
- }
-
- private static native void addArpEntry(byte[] ethAddr, byte[] netAddr, String ifname,
- FileDescriptor fd) throws IOException;
-
-
- /**
* Get the tcp repair window associated with the {@code fd}.
*
* @param fd the tcp socket's {@link FileDescriptor}.
diff --git a/core/java/android/net/util/SocketUtils.java b/core/java/android/net/util/SocketUtils.java
index 6f8aece..1364d8c 100644
--- a/core/java/android/net/util/SocketUtils.java
+++ b/core/java/android/net/util/SocketUtils.java
@@ -23,21 +23,17 @@
import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.annotation.TestApi;
-import android.net.MacAddress;
import android.net.NetworkUtils;
import android.system.ErrnoException;
import android.system.NetlinkSocketAddress;
import android.system.Os;
import android.system.PacketSocketAddress;
-import android.system.StructTimeval;
import libcore.io.IoBridge;
import java.io.FileDescriptor;
import java.io.IOException;
-import java.net.Inet4Address;
import java.net.SocketAddress;
-import java.net.SocketException;
/**
* Collection of utilities to interact with raw sockets.
@@ -85,57 +81,11 @@
}
/**
- * Set an option on a socket that takes a time value argument.
- */
- public static void setSocketTimeValueOption(
- @NonNull FileDescriptor fd, int level, int option, long millis) throws ErrnoException {
- Os.setsockoptTimeval(fd, level, option, StructTimeval.fromMillis(millis));
- }
-
- /**
* @see IoBridge#closeAndSignalBlockedThreads(FileDescriptor)
*/
public static void closeSocket(@Nullable FileDescriptor fd) throws IOException {
IoBridge.closeAndSignalBlockedThreads(fd);
}
- /**
- * Attaches a socket filter that accepts DHCP packets to the given socket.
- */
- public static void attachDhcpFilter(@NonNull FileDescriptor fd) throws SocketException {
- NetworkUtils.attachDhcpFilter(fd);
- }
-
- /**
- * Attaches a socket filter that accepts ICMPv6 router advertisements to the given socket.
- * @param fd the socket's {@link FileDescriptor}.
- * @param packetType the hardware address type, one of ARPHRD_*.
- */
- public static void attachRaFilter(@NonNull FileDescriptor fd, int packetType)
- throws SocketException {
- NetworkUtils.attachRaFilter(fd, packetType);
- }
-
- /**
- * Attaches a socket filter that accepts L2-L4 signaling traffic required for IP connectivity.
- *
- * This includes: all ARP, ICMPv6 RS/RA/NS/NA messages, and DHCPv4 exchanges.
- *
- * @param fd the socket's {@link FileDescriptor}.
- * @param packetType the hardware address type, one of ARPHRD_*.
- */
- public static void attachControlPacketFilter(@NonNull FileDescriptor fd, int packetType)
- throws SocketException {
- NetworkUtils.attachControlPacketFilter(fd, packetType);
- }
-
- /**
- * Add an entry into the ARP cache.
- */
- public static void addArpEntry(@NonNull Inet4Address ipv4Addr, @NonNull MacAddress ethAddr,
- @NonNull String ifname, @NonNull FileDescriptor fd) throws IOException {
- NetworkUtils.addArpEntry(ipv4Addr, ethAddr, ifname, fd);
- }
-
private SocketUtils() {}
}
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 2e1ef38..3db6b2b 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -5662,17 +5662,6 @@
private static final Validator ODI_CAPTIONS_ENABLED_VALIDATOR = BOOLEAN_VALIDATOR;
/**
- * Setting to indicate that on device captions cannot be shown because the app
- * which is currently playing media had opted out.
- *
- * @hide
- */
- @SystemApi
- public static final String ODI_CAPTIONS_OPTED_OUT = "odi_captions_opted_out";
-
- private static final Validator ODI_CAPTIONS_OPTED_OUT_VALIDATOR = BOOLEAN_VALIDATOR;
-
- /**
* On Android 8.0 (API level 26) and higher versions of the platform,
* a 64-bit number (expressed as a hexadecimal string), unique to
* each combination of app-signing key, user, and device.
@@ -8998,7 +8987,6 @@
VALIDATORS.put(SILENCE_CALL_GESTURE_COUNT, SILENCE_GESTURE_COUNT_VALIDATOR);
VALIDATORS.put(SILENCE_NOTIFICATION_GESTURE_COUNT, SILENCE_GESTURE_COUNT_VALIDATOR);
VALIDATORS.put(ODI_CAPTIONS_ENABLED, ODI_CAPTIONS_ENABLED_VALIDATOR);
- VALIDATORS.put(ODI_CAPTIONS_OPTED_OUT, ODI_CAPTIONS_OPTED_OUT_VALIDATOR);
}
/**
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 65fe87f..dfa51ff 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -9891,10 +9891,14 @@
info.setContentDescription(getContentDescription());
info.setEnabled(isEnabled());
+ info.setClickable(isClickable());
+ info.setFocusable(isFocusable());
info.setScreenReaderFocusable(isScreenReaderFocusable());
info.setFocused(isFocused());
info.setAccessibilityFocused(isAccessibilityFocused());
info.setSelected(isSelected());
+ info.setLongClickable(isLongClickable());
+ info.setContextClickable(isContextClickable());
info.setLiveRegion(getAccessibilityLiveRegion());
if ((mTooltipInfo != null) && (mTooltipInfo.mTooltipText != null)) {
info.setTooltipText(mTooltipInfo.mTooltipText);
diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfo.java b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
index 774a359..3b310fc 100644
--- a/core/java/android/view/accessibility/AccessibilityNodeInfo.java
+++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
@@ -611,14 +611,22 @@
private static final int BOOLEAN_PROPERTY_CHECKED = 0x00000002;
+ private static final int BOOLEAN_PROPERTY_FOCUSABLE = 0x00000004;
+
private static final int BOOLEAN_PROPERTY_FOCUSED = 0x00000008;
private static final int BOOLEAN_PROPERTY_SELECTED = 0x00000010;
+ private static final int BOOLEAN_PROPERTY_CLICKABLE = 0x00000020;
+
+ private static final int BOOLEAN_PROPERTY_LONG_CLICKABLE = 0x00000040;
+
private static final int BOOLEAN_PROPERTY_ENABLED = 0x00000080;
private static final int BOOLEAN_PROPERTY_PASSWORD = 0x00000100;
+ private static final int BOOLEAN_PROPERTY_SCROLLABLE = 0x00000200;
+
private static final int BOOLEAN_PROPERTY_ACCESSIBILITY_FOCUSED = 0x00000400;
private static final int BOOLEAN_PROPERTY_VISIBLE_TO_USER = 0x00000800;
@@ -633,6 +641,8 @@
private static final int BOOLEAN_PROPERTY_CONTENT_INVALID = 0x00010000;
+ private static final int BOOLEAN_PROPERTY_CONTEXT_CLICKABLE = 0x00020000;
+
private static final int BOOLEAN_PROPERTY_IMPORTANCE = 0x0040000;
private static final int BOOLEAN_PROPERTY_SCREEN_READER_FOCUSABLE = 0x0080000;
@@ -1191,16 +1201,6 @@
mActions.add(action);
}
- private boolean hasActionWithId(int actionId) {
- List<AccessibilityAction> actions = getActionList();
- for (int i = 0; i < actions.size(); i++) {
- if (actions.get(i).getId() == actionId) {
- return true;
- }
- }
- return false;
- }
-
/**
* Adds an action that can be performed on the node.
* <p>
@@ -1814,7 +1814,7 @@
* @return True if the node is focusable.
*/
public boolean isFocusable() {
- return hasActionWithId(ACTION_FOCUS) || hasActionWithId(ACTION_CLEAR_FOCUS);
+ return getBooleanProperty(BOOLEAN_PROPERTY_FOCUSABLE);
}
/**
@@ -1828,11 +1828,10 @@
* @param focusable True if the node is focusable.
*
* @throws IllegalStateException If called from an AccessibilityService.
- * @deprecated Use {@link #addAction(AccessibilityAction)}
- * with {@link AccessibilityAction#ACTION_FOCUS}
*/
- @Deprecated
- public void setFocusable(boolean focusable) { }
+ public void setFocusable(boolean focusable) {
+ setBooleanProperty(BOOLEAN_PROPERTY_FOCUSABLE, focusable);
+ }
/**
* Gets whether this node is focused.
@@ -1940,7 +1939,7 @@
* @return True if the node is clickable.
*/
public boolean isClickable() {
- return hasActionWithId(ACTION_CLICK);
+ return getBooleanProperty(BOOLEAN_PROPERTY_CLICKABLE);
}
/**
@@ -1954,11 +1953,10 @@
* @param clickable True if the node is clickable.
*
* @throws IllegalStateException If called from an AccessibilityService.
- * @deprecated Use {@link #addAction(AccessibilityAction)}
- * with {@link AccessibilityAction#ACTION_CLICK}
*/
- @Deprecated
- public void setClickable(boolean clickable) { }
+ public void setClickable(boolean clickable) {
+ setBooleanProperty(BOOLEAN_PROPERTY_CLICKABLE, clickable);
+ }
/**
* Gets whether this node is long clickable.
@@ -1966,7 +1964,7 @@
* @return True if the node is long clickable.
*/
public boolean isLongClickable() {
- return hasActionWithId(ACTION_LONG_CLICK);
+ return getBooleanProperty(BOOLEAN_PROPERTY_LONG_CLICKABLE);
}
/**
@@ -1980,11 +1978,10 @@
* @param longClickable True if the node is long clickable.
*
* @throws IllegalStateException If called from an AccessibilityService.
- * @deprecated Use {@link #addAction(AccessibilityAction)}
- * with {@link AccessibilityAction#ACTION_LONG_CLICK}
*/
- @Deprecated
- public void setLongClickable(boolean longClickable) { }
+ public void setLongClickable(boolean longClickable) {
+ setBooleanProperty(BOOLEAN_PROPERTY_LONG_CLICKABLE, longClickable);
+ }
/**
* Gets whether this node is enabled.
@@ -2042,13 +2039,7 @@
* @return True if the node is scrollable, false otherwise.
*/
public boolean isScrollable() {
- return hasActionWithId(ACTION_SCROLL_BACKWARD)
- || hasActionWithId(ACTION_SCROLL_FORWARD)
- || hasActionWithId(R.id.accessibilityActionScrollToPosition)
- || hasActionWithId(R.id.accessibilityActionScrollUp)
- || hasActionWithId(R.id.accessibilityActionScrollDown)
- || hasActionWithId(R.id.accessibilityActionScrollLeft)
- || hasActionWithId(R.id.accessibilityActionScrollRight);
+ return getBooleanProperty(BOOLEAN_PROPERTY_SCROLLABLE);
}
/**
@@ -2062,11 +2053,9 @@
* @param scrollable True if the node is scrollable, false otherwise.
*
* @throws IllegalStateException If called from an AccessibilityService.
- * @deprecated Use {@link #addAction(AccessibilityAction)}
*/
- @Deprecated
-
public void setScrollable(boolean scrollable) {
+ setBooleanProperty(BOOLEAN_PROPERTY_SCROLLABLE, scrollable);
}
/**
@@ -2257,7 +2246,7 @@
* @return True if the node is context clickable.
*/
public boolean isContextClickable() {
- return hasActionWithId(R.id.accessibilityActionContextClick);
+ return getBooleanProperty(BOOLEAN_PROPERTY_CONTEXT_CLICKABLE);
}
/**
@@ -2270,11 +2259,10 @@
*
* @param contextClickable True if the node is context clickable.
* @throws IllegalStateException If called from an AccessibilityService.
- * @deprecated Use {@link #addAction(AccessibilityAction)}
- * with {@link AccessibilityAction#ACTION_CONTEXT_CLICK}
*/
- @Deprecated
- public void setContextClickable(boolean contextClickable) { }
+ public void setContextClickable(boolean contextClickable) {
+ setBooleanProperty(BOOLEAN_PROPERTY_CONTEXT_CLICKABLE, contextClickable);
+ }
/**
* Gets the node's live region mode.
@@ -2368,7 +2356,7 @@
* @return If the node can be dismissed.
*/
public boolean isDismissable() {
- return hasActionWithId(ACTION_DISMISS);
+ return getBooleanProperty(BOOLEAN_PROPERTY_DISMISSABLE);
}
/**
@@ -2380,11 +2368,10 @@
* </p>
*
* @param dismissable If the node can be dismissed.
- * @deprecated Use {@link #addAction(AccessibilityAction)}
- * with {@link AccessibilityAction#ACTION_DISMISS}
*/
- @Deprecated
- public void setDismissable(boolean dismissable) { }
+ public void setDismissable(boolean dismissable) {
+ setBooleanProperty(BOOLEAN_PROPERTY_DISMISSABLE, dismissable);
+ }
/**
* Returns whether the node originates from a view considered important for accessibility.
diff --git a/core/jni/android_net_NetUtils.cpp b/core/jni/android_net_NetUtils.cpp
index 82acf6f..dd754f3 100644
--- a/core/jni/android_net_NetUtils.cpp
+++ b/core/jni/android_net_NetUtils.cpp
@@ -48,17 +48,6 @@
namespace android {
-static const uint32_t kEtherTypeOffset = offsetof(ether_header, ether_type);
-static const uint32_t kEtherHeaderLen = sizeof(ether_header);
-static const uint32_t kIPv4Protocol = kEtherHeaderLen + offsetof(iphdr, protocol);
-static const uint32_t kIPv4FlagsOffset = kEtherHeaderLen + offsetof(iphdr, frag_off);
-static const uint32_t kIPv6NextHeader = kEtherHeaderLen + offsetof(ip6_hdr, ip6_nxt);
-static const uint32_t kIPv6PayloadStart = kEtherHeaderLen + sizeof(ip6_hdr);
-static const uint32_t kICMPv6TypeOffset = kIPv6PayloadStart + offsetof(icmp6_hdr, icmp6_type);
-static const uint32_t kUDPSrcPortIndirectOffset = kEtherHeaderLen + offsetof(udphdr, source);
-static const uint32_t kUDPDstPortIndirectOffset = kEtherHeaderLen + offsetof(udphdr, dest);
-static const uint16_t kDhcpClientPort = 68;
-
constexpr int MAXPACKETSIZE = 8 * 1024;
// FrameworkListener limits the size of commands to 1024 bytes. TODO: fix this.
constexpr int MAXCMDSIZE = 1024;
@@ -84,149 +73,6 @@
env->Throw(reinterpret_cast<jthrowable>(exception));
}
-static void android_net_utils_attachDhcpFilter(JNIEnv *env, jobject clazz, jobject javaFd)
-{
- struct sock_filter filter_code[] = {
- // Check the protocol is UDP.
- BPF_STMT(BPF_LD | BPF_B | BPF_ABS, kIPv4Protocol),
- BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, IPPROTO_UDP, 0, 6),
-
- // Check this is not a fragment.
- BPF_STMT(BPF_LD | BPF_H | BPF_ABS, kIPv4FlagsOffset),
- BPF_JUMP(BPF_JMP | BPF_JSET | BPF_K, IP_OFFMASK, 4, 0),
-
- // Get the IP header length.
- BPF_STMT(BPF_LDX | BPF_B | BPF_MSH, kEtherHeaderLen),
-
- // Check the destination port.
- BPF_STMT(BPF_LD | BPF_H | BPF_IND, kUDPDstPortIndirectOffset),
- BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, kDhcpClientPort, 0, 1),
-
- // Accept or reject.
- BPF_STMT(BPF_RET | BPF_K, 0xffff),
- BPF_STMT(BPF_RET | BPF_K, 0)
- };
- struct sock_fprog filter = {
- sizeof(filter_code) / sizeof(filter_code[0]),
- filter_code,
- };
-
- int fd = jniGetFDFromFileDescriptor(env, javaFd);
- if (setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &filter, sizeof(filter)) != 0) {
- jniThrowExceptionFmt(env, "java/net/SocketException",
- "setsockopt(SO_ATTACH_FILTER): %s", strerror(errno));
- }
-}
-
-static void android_net_utils_attachRaFilter(JNIEnv *env, jobject clazz, jobject javaFd,
- jint hardwareAddressType)
-{
- if (hardwareAddressType != ARPHRD_ETHER) {
- jniThrowExceptionFmt(env, "java/net/SocketException",
- "attachRaFilter only supports ARPHRD_ETHER");
- return;
- }
-
- struct sock_filter filter_code[] = {
- // Check IPv6 Next Header is ICMPv6.
- BPF_STMT(BPF_LD | BPF_B | BPF_ABS, kIPv6NextHeader),
- BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, IPPROTO_ICMPV6, 0, 3),
-
- // Check ICMPv6 type is Router Advertisement.
- BPF_STMT(BPF_LD | BPF_B | BPF_ABS, kICMPv6TypeOffset),
- BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, ND_ROUTER_ADVERT, 0, 1),
-
- // Accept or reject.
- BPF_STMT(BPF_RET | BPF_K, 0xffff),
- BPF_STMT(BPF_RET | BPF_K, 0)
- };
- struct sock_fprog filter = {
- sizeof(filter_code) / sizeof(filter_code[0]),
- filter_code,
- };
-
- int fd = jniGetFDFromFileDescriptor(env, javaFd);
- if (setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &filter, sizeof(filter)) != 0) {
- jniThrowExceptionFmt(env, "java/net/SocketException",
- "setsockopt(SO_ATTACH_FILTER): %s", strerror(errno));
- }
-}
-
-// TODO: Move all this filter code into libnetutils.
-static void android_net_utils_attachControlPacketFilter(
- JNIEnv *env, jobject clazz, jobject javaFd, jint hardwareAddressType) {
- if (hardwareAddressType != ARPHRD_ETHER) {
- jniThrowExceptionFmt(env, "java/net/SocketException",
- "attachControlPacketFilter only supports ARPHRD_ETHER");
- return;
- }
-
- // Capture all:
- // - ARPs
- // - DHCPv4 packets
- // - Router Advertisements & Solicitations
- // - Neighbor Advertisements & Solicitations
- //
- // tcpdump:
- // arp or
- // '(ip and udp port 68)' or
- // '(icmp6 and ip6[40] >= 133 and ip6[40] <= 136)'
- struct sock_filter filter_code[] = {
- // Load the link layer next payload field.
- BPF_STMT(BPF_LD | BPF_H | BPF_ABS, kEtherTypeOffset),
-
- // Accept all ARP.
- // TODO: Figure out how to better filter ARPs on noisy networks.
- BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, ETHERTYPE_ARP, 16, 0),
-
- // If IPv4:
- BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, ETHERTYPE_IP, 0, 9),
-
- // Check the protocol is UDP.
- BPF_STMT(BPF_LD | BPF_B | BPF_ABS, kIPv4Protocol),
- BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, IPPROTO_UDP, 0, 14),
-
- // Check this is not a fragment.
- BPF_STMT(BPF_LD | BPF_H | BPF_ABS, kIPv4FlagsOffset),
- BPF_JUMP(BPF_JMP | BPF_JSET | BPF_K, IP_OFFMASK, 12, 0),
-
- // Get the IP header length.
- BPF_STMT(BPF_LDX | BPF_B | BPF_MSH, kEtherHeaderLen),
-
- // Check the source port.
- BPF_STMT(BPF_LD | BPF_H | BPF_IND, kUDPSrcPortIndirectOffset),
- BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, kDhcpClientPort, 8, 0),
-
- // Check the destination port.
- BPF_STMT(BPF_LD | BPF_H | BPF_IND, kUDPDstPortIndirectOffset),
- BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, kDhcpClientPort, 6, 7),
-
- // IPv6 ...
- BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, ETHERTYPE_IPV6, 0, 6),
- // ... check IPv6 Next Header is ICMPv6 (ignore fragments), ...
- BPF_STMT(BPF_LD | BPF_B | BPF_ABS, kIPv6NextHeader),
- BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, IPPROTO_ICMPV6, 0, 4),
- // ... and check the ICMPv6 type is one of RS/RA/NS/NA.
- BPF_STMT(BPF_LD | BPF_B | BPF_ABS, kICMPv6TypeOffset),
- BPF_JUMP(BPF_JMP | BPF_JGE | BPF_K, ND_ROUTER_SOLICIT, 0, 2),
- BPF_JUMP(BPF_JMP | BPF_JGT | BPF_K, ND_NEIGHBOR_ADVERT, 1, 0),
-
- // Accept or reject.
- BPF_STMT(BPF_RET | BPF_K, 0xffff),
- BPF_STMT(BPF_RET | BPF_K, 0)
- };
- struct sock_fprog filter = {
- sizeof(filter_code) / sizeof(filter_code[0]),
- filter_code,
- };
-
- int fd = jniGetFDFromFileDescriptor(env, javaFd);
- if (setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &filter, sizeof(filter)) != 0) {
- jniThrowExceptionFmt(env, "java/net/SocketException",
- "setsockopt(SO_ATTACH_FILTER): %s", strerror(errno));
- }
-}
-
static void android_net_utils_attachDropAllBPFFilter(JNIEnv *env, jobject clazz, jobject javaFd)
{
struct sock_filter filter_code[] = {
@@ -389,46 +235,6 @@
return true;
}
-static void android_net_utils_addArpEntry(JNIEnv *env, jobject thiz, jbyteArray ethAddr,
- jbyteArray ipv4Addr, jstring ifname, jobject javaFd)
-{
- struct arpreq req = {};
- struct sockaddr_in& netAddrStruct = *reinterpret_cast<sockaddr_in*>(&req.arp_pa);
- struct sockaddr& ethAddrStruct = req.arp_ha;
-
- ethAddrStruct.sa_family = ARPHRD_ETHER;
- if (!checkLenAndCopy(env, ethAddr, ETH_ALEN, ethAddrStruct.sa_data)) {
- jniThrowException(env, "java/io/IOException", "Invalid ethAddr length");
- return;
- }
-
- netAddrStruct.sin_family = AF_INET;
- if (!checkLenAndCopy(env, ipv4Addr, sizeof(in_addr), &netAddrStruct.sin_addr)) {
- jniThrowException(env, "java/io/IOException", "Invalid ipv4Addr length");
- return;
- }
-
- int ifLen = env->GetStringLength(ifname);
- // IFNAMSIZ includes the terminating NULL character
- if (ifLen >= IFNAMSIZ) {
- jniThrowException(env, "java/io/IOException", "ifname too long");
- return;
- }
- env->GetStringUTFRegion(ifname, 0, ifLen, req.arp_dev);
-
- req.arp_flags = ATF_COM; // Completed entry (ha valid)
- int fd = jniGetFDFromFileDescriptor(env, javaFd);
- if (fd < 0) {
- jniThrowExceptionFmt(env, "java/io/IOException", "Invalid file descriptor");
- return;
- }
- // See also: man 7 arp
- if (ioctl(fd, SIOCSARP, &req)) {
- jniThrowExceptionFmt(env, "java/io/IOException", "ioctl error: %s", strerror(errno));
- return;
- }
-}
-
static jobject android_net_utils_resNetworkQuery(JNIEnv *env, jobject thiz, jint netId,
jstring dname, jint ns_class, jint ns_type, jint flags) {
const jsize javaCharsCount = env->GetStringLength(dname);
@@ -542,10 +348,6 @@
{ "bindSocketToNetwork", "(II)I", (void*) android_net_utils_bindSocketToNetwork },
{ "protectFromVpn", "(I)Z", (void*)android_net_utils_protectFromVpn },
{ "queryUserAccess", "(II)Z", (void*)android_net_utils_queryUserAccess },
- { "addArpEntry", "([B[BLjava/lang/String;Ljava/io/FileDescriptor;)V", (void*) android_net_utils_addArpEntry },
- { "attachDhcpFilter", "(Ljava/io/FileDescriptor;)V", (void*) android_net_utils_attachDhcpFilter },
- { "attachRaFilter", "(Ljava/io/FileDescriptor;I)V", (void*) android_net_utils_attachRaFilter },
- { "attachControlPacketFilter", "(Ljava/io/FileDescriptor;I)V", (void*) android_net_utils_attachControlPacketFilter },
{ "attachDropAllBPFFilter", "(Ljava/io/FileDescriptor;)V", (void*) android_net_utils_attachDropAllBPFFilter },
{ "detachBPFFilter", "(Ljava/io/FileDescriptor;)V", (void*) android_net_utils_detachBPFFilter },
{ "getTcpRepairWindow", "(Ljava/io/FileDescriptor;)Landroid/net/TcpRepairWindow;", (void*) android_net_utils_getTcpRepairWindow },
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 21f5acb..e8cc96c 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -3558,6 +3558,12 @@
-->
<string name="config_defaultSystemCaptionsService" translatable="false"></string>
+ <!-- The component name for the system-wide captions manager service.
+ This service must be trusted, as the system binds to it and keeps it running.
+ Example: "com.android.captions/.SystemCaptionsManagerService"
+ -->
+ <string name="config_defaultSystemCaptionsManagerService" translatable="false"></string>
+
<!-- The package name for the incident report approver app.
This app is usually PermissionController or an app that replaces it. When
a bugreport or incident report with EXPLICT-level sharing flags is going to be
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index a6841d4..664059a 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -3409,6 +3409,7 @@
<java-symbol type="string" name="config_defaultContentSuggestionsService" />
<java-symbol type="string" name="config_defaultAttentionService" />
<java-symbol type="string" name="config_defaultSystemCaptionsService" />
+ <java-symbol type="string" name="config_defaultSystemCaptionsManagerService" />
<java-symbol type="string" name="notification_channel_foreground_service" />
<java-symbol type="string" name="foreground_service_app_in_background" />
diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
index 33c23c4..8cc6e37 100644
--- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java
+++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
@@ -649,7 +649,6 @@
Settings.Secure.NIGHT_DISPLAY_LAST_ACTIVATED_TIME,
Settings.Secure.NUM_ROTATION_SUGGESTIONS_ACCEPTED,
Settings.Secure.ODI_CAPTIONS_ENABLED,
- Settings.Secure.ODI_CAPTIONS_OPTED_OUT,
Settings.Secure.PACKAGE_VERIFIER_STATE,
Settings.Secure.PACKAGE_VERIFIER_USER_CONSENT,
Settings.Secure.PARENTAL_CONTROL_LAST_UPDATE,
diff --git a/core/tests/coretests/src/android/view/accessibility/AccessibilityNodeInfoTest.java b/core/tests/coretests/src/android/view/accessibility/AccessibilityNodeInfoTest.java
index 0ed690c..4c59207 100644
--- a/core/tests/coretests/src/android/view/accessibility/AccessibilityNodeInfoTest.java
+++ b/core/tests/coretests/src/android/view/accessibility/AccessibilityNodeInfoTest.java
@@ -58,7 +58,7 @@
// The number of flags held in boolean properties. Their values should also be double-checked
// in the methods above.
- private static final int NUM_BOOLEAN_PROPERTIES = 18;
+ private static final int NUM_BOOLEAN_PROPERTIES = 23;
@Test
public void testStandardActions_serializationFlagIsValid() {
diff --git a/packages/NetworkStack/Android.bp b/packages/NetworkStack/Android.bp
index 57a3db5..262e6f6 100644
--- a/packages/NetworkStack/Android.bp
+++ b/packages/NetworkStack/Android.bp
@@ -49,6 +49,27 @@
manifest: "AndroidManifestBase.xml",
}
+cc_library_shared {
+ name: "libnetworkstackutilsjni",
+ srcs: [
+ "jni/network_stack_utils_jni.cpp"
+ ],
+
+ shared_libs: [
+ "liblog",
+ "libcutils",
+ "libnativehelper",
+ ],
+ static_libs: [
+ "libpcap",
+ ],
+ cflags: [
+ "-Wall",
+ "-Werror",
+ "-Wno-unused-parameter",
+ ],
+}
+
java_defaults {
name: "NetworkStackAppCommon",
defaults: ["NetworkStackCommon"],
@@ -56,6 +77,7 @@
static_libs: [
"NetworkStackBase",
],
+ jni_libs: ["libnetworkstackutilsjni"],
// Resources already included in NetworkStackBase
resource_dirs: [],
jarjar_rules: "jarjar-rules-shared.txt",
diff --git a/packages/NetworkStack/jni/network_stack_utils_jni.cpp b/packages/NetworkStack/jni/network_stack_utils_jni.cpp
new file mode 100644
index 0000000..5544eaa
--- /dev/null
+++ b/packages/NetworkStack/jni/network_stack_utils_jni.cpp
@@ -0,0 +1,264 @@
+/*
+ * Copyright 2019, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "NetworkStackUtils-JNI"
+
+#include <errno.h>
+#include <jni.h>
+#include <linux/filter.h>
+#include <linux/if_arp.h>
+#include <net/if.h>
+#include <netinet/ether.h>
+#include <netinet/icmp6.h>
+#include <netinet/ip.h>
+#include <netinet/ip6.h>
+#include <netinet/udp.h>
+#include <stdlib.h>
+
+#include <string>
+
+#include <nativehelper/JNIHelp.h>
+#include <utils/Log.h>
+
+namespace android {
+constexpr const char NETWORKSTACKUTILS_PKG_NAME[] = "android/net/util/NetworkStackUtils";
+
+static const uint32_t kEtherTypeOffset = offsetof(ether_header, ether_type);
+static const uint32_t kEtherHeaderLen = sizeof(ether_header);
+static const uint32_t kIPv4Protocol = kEtherHeaderLen + offsetof(iphdr, protocol);
+static const uint32_t kIPv4FlagsOffset = kEtherHeaderLen + offsetof(iphdr, frag_off);
+static const uint32_t kIPv6NextHeader = kEtherHeaderLen + offsetof(ip6_hdr, ip6_nxt);
+static const uint32_t kIPv6PayloadStart = kEtherHeaderLen + sizeof(ip6_hdr);
+static const uint32_t kICMPv6TypeOffset = kIPv6PayloadStart + offsetof(icmp6_hdr, icmp6_type);
+static const uint32_t kUDPSrcPortIndirectOffset = kEtherHeaderLen + offsetof(udphdr, source);
+static const uint32_t kUDPDstPortIndirectOffset = kEtherHeaderLen + offsetof(udphdr, dest);
+static const uint16_t kDhcpClientPort = 68;
+
+static bool checkLenAndCopy(JNIEnv* env, const jbyteArray& addr, int len, void* dst) {
+ if (env->GetArrayLength(addr) != len) {
+ return false;
+ }
+ env->GetByteArrayRegion(addr, 0, len, reinterpret_cast<jbyte*>(dst));
+ return true;
+}
+
+static void network_stack_utils_addArpEntry(JNIEnv *env, jobject thiz, jbyteArray ethAddr,
+ jbyteArray ipv4Addr, jstring ifname, jobject javaFd) {
+ arpreq req = {};
+ sockaddr_in& netAddrStruct = *reinterpret_cast<sockaddr_in*>(&req.arp_pa);
+ sockaddr& ethAddrStruct = req.arp_ha;
+
+ ethAddrStruct.sa_family = ARPHRD_ETHER;
+ if (!checkLenAndCopy(env, ethAddr, ETH_ALEN, ethAddrStruct.sa_data)) {
+ jniThrowException(env, "java/io/IOException", "Invalid ethAddr length");
+ return;
+ }
+
+ netAddrStruct.sin_family = AF_INET;
+ if (!checkLenAndCopy(env, ipv4Addr, sizeof(in_addr), &netAddrStruct.sin_addr)) {
+ jniThrowException(env, "java/io/IOException", "Invalid ipv4Addr length");
+ return;
+ }
+
+ int ifLen = env->GetStringLength(ifname);
+ // IFNAMSIZ includes the terminating NULL character
+ if (ifLen >= IFNAMSIZ) {
+ jniThrowException(env, "java/io/IOException", "ifname too long");
+ return;
+ }
+ env->GetStringUTFRegion(ifname, 0, ifLen, req.arp_dev);
+
+ req.arp_flags = ATF_COM; // Completed entry (ha valid)
+ int fd = jniGetFDFromFileDescriptor(env, javaFd);
+ if (fd < 0) {
+ jniThrowExceptionFmt(env, "java/io/IOException", "Invalid file descriptor");
+ return;
+ }
+ // See also: man 7 arp
+ if (ioctl(fd, SIOCSARP, &req)) {
+ jniThrowExceptionFmt(env, "java/io/IOException", "ioctl error: %s", strerror(errno));
+ return;
+ }
+}
+
+static void network_stack_utils_attachDhcpFilter(JNIEnv *env, jobject clazz, jobject javaFd) {
+ static sock_filter filter_code[] = {
+ // Check the protocol is UDP.
+ BPF_STMT(BPF_LD | BPF_B | BPF_ABS, kIPv4Protocol),
+ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, IPPROTO_UDP, 0, 6),
+
+ // Check this is not a fragment.
+ BPF_STMT(BPF_LD | BPF_H | BPF_ABS, kIPv4FlagsOffset),
+ BPF_JUMP(BPF_JMP | BPF_JSET | BPF_K, IP_OFFMASK, 4, 0),
+
+ // Get the IP header length.
+ BPF_STMT(BPF_LDX | BPF_B | BPF_MSH, kEtherHeaderLen),
+
+ // Check the destination port.
+ BPF_STMT(BPF_LD | BPF_H | BPF_IND, kUDPDstPortIndirectOffset),
+ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, kDhcpClientPort, 0, 1),
+
+ // Accept or reject.
+ BPF_STMT(BPF_RET | BPF_K, 0xffff),
+ BPF_STMT(BPF_RET | BPF_K, 0)
+ };
+ static const sock_fprog filter = {
+ sizeof(filter_code) / sizeof(filter_code[0]),
+ filter_code,
+ };
+
+ int fd = jniGetFDFromFileDescriptor(env, javaFd);
+ if (setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &filter, sizeof(filter)) != 0) {
+ jniThrowExceptionFmt(env, "java/net/SocketException",
+ "setsockopt(SO_ATTACH_FILTER): %s", strerror(errno));
+ }
+}
+
+static void network_stack_utils_attachRaFilter(JNIEnv *env, jobject clazz, jobject javaFd,
+ jint hardwareAddressType) {
+ if (hardwareAddressType != ARPHRD_ETHER) {
+ jniThrowExceptionFmt(env, "java/net/SocketException",
+ "attachRaFilter only supports ARPHRD_ETHER");
+ return;
+ }
+
+ static sock_filter filter_code[] = {
+ // Check IPv6 Next Header is ICMPv6.
+ BPF_STMT(BPF_LD | BPF_B | BPF_ABS, kIPv6NextHeader),
+ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, IPPROTO_ICMPV6, 0, 3),
+
+ // Check ICMPv6 type is Router Advertisement.
+ BPF_STMT(BPF_LD | BPF_B | BPF_ABS, kICMPv6TypeOffset),
+ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, ND_ROUTER_ADVERT, 0, 1),
+
+ // Accept or reject.
+ BPF_STMT(BPF_RET | BPF_K, 0xffff),
+ BPF_STMT(BPF_RET | BPF_K, 0)
+ };
+ static const sock_fprog filter = {
+ sizeof(filter_code) / sizeof(filter_code[0]),
+ filter_code,
+ };
+
+ int fd = jniGetFDFromFileDescriptor(env, javaFd);
+ if (setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &filter, sizeof(filter)) != 0) {
+ jniThrowExceptionFmt(env, "java/net/SocketException",
+ "setsockopt(SO_ATTACH_FILTER): %s", strerror(errno));
+ }
+}
+
+// TODO: Move all this filter code into libnetutils.
+static void network_stack_utils_attachControlPacketFilter(
+ JNIEnv *env, jobject clazz, jobject javaFd, jint hardwareAddressType) {
+ if (hardwareAddressType != ARPHRD_ETHER) {
+ jniThrowExceptionFmt(env, "java/net/SocketException",
+ "attachControlPacketFilter only supports ARPHRD_ETHER");
+ return;
+ }
+
+ // Capture all:
+ // - ARPs
+ // - DHCPv4 packets
+ // - Router Advertisements & Solicitations
+ // - Neighbor Advertisements & Solicitations
+ //
+ // tcpdump:
+ // arp or
+ // '(ip and udp port 68)' or
+ // '(icmp6 and ip6[40] >= 133 and ip6[40] <= 136)'
+ static sock_filter filter_code[] = {
+ // Load the link layer next payload field.
+ BPF_STMT(BPF_LD | BPF_H | BPF_ABS, kEtherTypeOffset),
+
+ // Accept all ARP.
+ // TODO: Figure out how to better filter ARPs on noisy networks.
+ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, ETHERTYPE_ARP, 16, 0),
+
+ // If IPv4:
+ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, ETHERTYPE_IP, 0, 9),
+
+ // Check the protocol is UDP.
+ BPF_STMT(BPF_LD | BPF_B | BPF_ABS, kIPv4Protocol),
+ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, IPPROTO_UDP, 0, 14),
+
+ // Check this is not a fragment.
+ BPF_STMT(BPF_LD | BPF_H | BPF_ABS, kIPv4FlagsOffset),
+ BPF_JUMP(BPF_JMP | BPF_JSET | BPF_K, IP_OFFMASK, 12, 0),
+
+ // Get the IP header length.
+ BPF_STMT(BPF_LDX | BPF_B | BPF_MSH, kEtherHeaderLen),
+
+ // Check the source port.
+ BPF_STMT(BPF_LD | BPF_H | BPF_IND, kUDPSrcPortIndirectOffset),
+ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, kDhcpClientPort, 8, 0),
+
+ // Check the destination port.
+ BPF_STMT(BPF_LD | BPF_H | BPF_IND, kUDPDstPortIndirectOffset),
+ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, kDhcpClientPort, 6, 7),
+
+ // IPv6 ...
+ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, ETHERTYPE_IPV6, 0, 6),
+ // ... check IPv6 Next Header is ICMPv6 (ignore fragments), ...
+ BPF_STMT(BPF_LD | BPF_B | BPF_ABS, kIPv6NextHeader),
+ BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, IPPROTO_ICMPV6, 0, 4),
+ // ... and check the ICMPv6 type is one of RS/RA/NS/NA.
+ BPF_STMT(BPF_LD | BPF_B | BPF_ABS, kICMPv6TypeOffset),
+ BPF_JUMP(BPF_JMP | BPF_JGE | BPF_K, ND_ROUTER_SOLICIT, 0, 2),
+ BPF_JUMP(BPF_JMP | BPF_JGT | BPF_K, ND_NEIGHBOR_ADVERT, 1, 0),
+
+ // Accept or reject.
+ BPF_STMT(BPF_RET | BPF_K, 0xffff),
+ BPF_STMT(BPF_RET | BPF_K, 0)
+ };
+ static const sock_fprog filter = {
+ sizeof(filter_code) / sizeof(filter_code[0]),
+ filter_code,
+ };
+
+ int fd = jniGetFDFromFileDescriptor(env, javaFd);
+ if (setsockopt(fd, SOL_SOCKET, SO_ATTACH_FILTER, &filter, sizeof(filter)) != 0) {
+ jniThrowExceptionFmt(env, "java/net/SocketException",
+ "setsockopt(SO_ATTACH_FILTER): %s", strerror(errno));
+ }
+}
+
+/*
+ * JNI registration.
+ */
+static const JNINativeMethod gNetworkStackUtilsMethods[] = {
+ /* name, signature, funcPtr */
+ { "addArpEntry", "([B[BLjava/lang/String;Ljava/io/FileDescriptor;)V", (void*) network_stack_utils_addArpEntry },
+ { "attachDhcpFilter", "(Ljava/io/FileDescriptor;)V", (void*) network_stack_utils_attachDhcpFilter },
+ { "attachRaFilter", "(Ljava/io/FileDescriptor;I)V", (void*) network_stack_utils_attachRaFilter },
+ { "attachControlPacketFilter", "(Ljava/io/FileDescriptor;I)V", (void*) network_stack_utils_attachControlPacketFilter },
+};
+
+extern "C" jint JNI_OnLoad(JavaVM* vm, void*) {
+ JNIEnv *env;
+ if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) {
+ ALOGE("ERROR: GetEnv failed");
+ return JNI_ERR;
+ }
+
+ if (jniRegisterNativeMethods(env, NETWORKSTACKUTILS_PKG_NAME,
+ gNetworkStackUtilsMethods, NELEM(gNetworkStackUtilsMethods)) < 0) {
+ return JNI_ERR;
+ }
+
+ return JNI_VERSION_1_6;
+
+}
+}; // namespace android
\ No newline at end of file
diff --git a/packages/NetworkStack/src/android/net/apf/ApfFilter.java b/packages/NetworkStack/src/android/net/apf/ApfFilter.java
index d2f3259..663e2f1 100644
--- a/packages/NetworkStack/src/android/net/apf/ApfFilter.java
+++ b/packages/NetworkStack/src/android/net/apf/ApfFilter.java
@@ -49,7 +49,6 @@
import android.net.metrics.RaEvent;
import android.net.util.InterfaceParams;
import android.net.util.NetworkStackUtils;
-import android.net.util.SocketUtils;
import android.os.PowerManager;
import android.os.SystemClock;
import android.system.ErrnoException;
@@ -478,7 +477,7 @@
SocketAddress addr = makePacketSocketAddress(
(short) ETH_P_IPV6, mInterfaceParams.index);
Os.bind(socket, addr);
- SocketUtils.attachRaFilter(socket, mApfCapabilities.apfPacketFormat);
+ NetworkStackUtils.attachRaFilter(socket, mApfCapabilities.apfPacketFormat);
} catch(SocketException|ErrnoException e) {
Log.e(TAG, "Error starting filter", e);
return;
diff --git a/packages/NetworkStack/src/android/net/dhcp/DhcpClient.java b/packages/NetworkStack/src/android/net/dhcp/DhcpClient.java
index 79d6a55..64adc0d 100644
--- a/packages/NetworkStack/src/android/net/dhcp/DhcpClient.java
+++ b/packages/NetworkStack/src/android/net/dhcp/DhcpClient.java
@@ -51,6 +51,7 @@
import android.net.metrics.DhcpErrorEvent;
import android.net.metrics.IpConnectivityLog;
import android.net.util.InterfaceParams;
+import android.net.util.NetworkStackUtils;
import android.net.util.SocketUtils;
import android.os.Message;
import android.os.SystemClock;
@@ -319,7 +320,7 @@
mPacketSock = Os.socket(AF_PACKET, SOCK_RAW, ETH_P_IP);
SocketAddress addr = makePacketSocketAddress((short) ETH_P_IP, mIface.index);
Os.bind(mPacketSock, addr);
- SocketUtils.attachDhcpFilter(mPacketSock);
+ NetworkStackUtils.attachDhcpFilter(mPacketSock);
} catch(SocketException|ErrnoException e) {
Log.e(TAG, "Error creating packet socket", e);
return false;
diff --git a/packages/NetworkStack/src/android/net/dhcp/DhcpServer.java b/packages/NetworkStack/src/android/net/dhcp/DhcpServer.java
index cd993e9..8832eaa 100644
--- a/packages/NetworkStack/src/android/net/dhcp/DhcpServer.java
+++ b/packages/NetworkStack/src/android/net/dhcp/DhcpServer.java
@@ -45,6 +45,7 @@
import android.net.INetworkStackStatusCallback;
import android.net.MacAddress;
import android.net.TrafficStats;
+import android.net.util.NetworkStackUtils;
import android.net.util.SharedLog;
import android.net.util.SocketUtils;
import android.os.Handler;
@@ -207,7 +208,7 @@
@Override
public void addArpEntry(@NonNull Inet4Address ipv4Addr, @NonNull MacAddress ethAddr,
@NonNull String ifname, @NonNull FileDescriptor fd) throws IOException {
- SocketUtils.addArpEntry(ipv4Addr, ethAddr, ifname, fd);
+ NetworkStackUtils.addArpEntry(ipv4Addr, ethAddr, ifname, fd);
}
@Override
diff --git a/packages/NetworkStack/src/android/net/ip/ConnectivityPacketTracker.java b/packages/NetworkStack/src/android/net/ip/ConnectivityPacketTracker.java
index de54824..eb49218 100644
--- a/packages/NetworkStack/src/android/net/ip/ConnectivityPacketTracker.java
+++ b/packages/NetworkStack/src/android/net/ip/ConnectivityPacketTracker.java
@@ -25,8 +25,8 @@
import android.net.util.ConnectivityPacketSummary;
import android.net.util.InterfaceParams;
+import android.net.util.NetworkStackUtils;
import android.net.util.PacketReader;
-import android.net.util.SocketUtils;
import android.os.Handler;
import android.system.ErrnoException;
import android.system.Os;
@@ -103,7 +103,7 @@
FileDescriptor s = null;
try {
s = Os.socket(AF_PACKET, SOCK_RAW | SOCK_NONBLOCK, 0);
- SocketUtils.attachControlPacketFilter(s, ARPHRD_ETHER);
+ NetworkStackUtils.attachControlPacketFilter(s, ARPHRD_ETHER);
Os.bind(s, makePacketSocketAddress((short) ETH_P_ALL, mInterface.index));
} catch (ErrnoException | IOException e) {
logError("Failed to create packet tracking socket: ", e);
diff --git a/packages/NetworkStack/src/android/net/util/NetworkStackUtils.java b/packages/NetworkStack/src/android/net/util/NetworkStackUtils.java
index 9d2df57..dada61c 100644
--- a/packages/NetworkStack/src/android/net/util/NetworkStackUtils.java
+++ b/packages/NetworkStack/src/android/net/util/NetworkStackUtils.java
@@ -23,14 +23,18 @@
import java.io.FileDescriptor;
import java.io.IOException;
+import java.net.Inet4Address;
+import java.net.SocketException;
import java.util.List;
import java.util.function.Predicate;
-
/**
* Collection of utilities for the network stack.
*/
public class NetworkStackUtils {
+ static {
+ System.loadLibrary("networkstackutilsjni");
+ }
/**
* @return True if the array is null or 0-length.
@@ -98,4 +102,39 @@
String value = DeviceConfig.getProperty(namespace, name);
return value != null ? value : defaultValue;
}
+
+ /**
+ * Attaches a socket filter that accepts DHCP packets to the given socket.
+ */
+ public static native void attachDhcpFilter(FileDescriptor fd) throws SocketException;
+
+ /**
+ * Attaches a socket filter that accepts ICMPv6 router advertisements to the given socket.
+ * @param fd the socket's {@link FileDescriptor}.
+ * @param packetType the hardware address type, one of ARPHRD_*.
+ */
+ public static native void attachRaFilter(FileDescriptor fd, int packetType)
+ throws SocketException;
+
+ /**
+ * Attaches a socket filter that accepts L2-L4 signaling traffic required for IP connectivity.
+ *
+ * This includes: all ARP, ICMPv6 RS/RA/NS/NA messages, and DHCPv4 exchanges.
+ *
+ * @param fd the socket's {@link FileDescriptor}.
+ * @param packetType the hardware address type, one of ARPHRD_*.
+ */
+ public static native void attachControlPacketFilter(FileDescriptor fd, int packetType)
+ throws SocketException;
+
+ /**
+ * Add an entry into the ARP cache.
+ */
+ public static void addArpEntry(Inet4Address ipv4Addr, android.net.MacAddress ethAddr,
+ String ifname, FileDescriptor fd) throws IOException {
+ addArpEntry(ethAddr.toByteArray(), ipv4Addr.getAddress(), ifname, fd);
+ }
+
+ private static native void addArpEntry(byte[] ethAddr, byte[] netAddr, String ifname,
+ FileDescriptor fd) throws IOException;
}
diff --git a/packages/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java b/packages/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java
index 6f31f9b..8f7d988 100644
--- a/packages/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java
+++ b/packages/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java
@@ -779,6 +779,7 @@
@Override
public void exit() {
+ mLaunchCaptivePortalAppBroadcastReceiver = null;
hideProvisioningNotification();
}
}
@@ -902,9 +903,10 @@
mLaunchCaptivePortalAppBroadcastReceiver = new CustomIntentReceiver(
ACTION_LAUNCH_CAPTIVE_PORTAL_APP, new Random().nextInt(),
CMD_LAUNCH_CAPTIVE_PORTAL_APP);
+ // Display the sign in notification.
+ // Only do this once for every time we enter MaybeNotifyState. b/122164725
+ showProvisioningNotification(mLaunchCaptivePortalAppBroadcastReceiver.mAction);
}
- // Display the sign in notification.
- showProvisioningNotification(mLaunchCaptivePortalAppBroadcastReceiver.mAction);
// Retest for captive portal occasionally.
sendMessageDelayed(CMD_CAPTIVE_PORTAL_RECHECK, 0 /* no UID */,
CAPTIVE_PORTAL_REEVALUATE_DELAY_MS);
diff --git a/packages/NetworkStack/tests/Android.bp b/packages/NetworkStack/tests/Android.bp
index d0f419c..fe3c1e8 100644
--- a/packages/NetworkStack/tests/Android.bp
+++ b/packages/NetworkStack/tests/Android.bp
@@ -57,6 +57,7 @@
"liblzma",
"libnativehelper",
"libnetworkstacktestsjni",
+ "libnetworkstackutilsjni",
"libpackagelistparser",
"libpcre2",
"libprocessgroup",
diff --git a/packages/SystemUI/res/layout/global_actions_grid_item.xml b/packages/SystemUI/res/layout/global_actions_grid_item.xml
index 999c7b8..e92ec0f 100644
--- a/packages/SystemUI/res/layout/global_actions_grid_item.xml
+++ b/packages/SystemUI/res/layout/global_actions_grid_item.xml
@@ -43,6 +43,9 @@
android:id="@*android:id/message"
android:layout_width="match_parent"
android:layout_height="wrap_content"
+ android:ellipsize="marquee"
+ android:marqueeRepeatLimit="marquee_forever"
+ android:singleLine="true"
android:gravity="center"
android:textSize="12dp"
android:textAppearance="?android:attr/textAppearanceSmall"
diff --git a/packages/SystemUI/res/layout/global_actions_wrapped.xml b/packages/SystemUI/res/layout/global_actions_wrapped.xml
index f932303..d441070 100644
--- a/packages/SystemUI/res/layout/global_actions_wrapped.xml
+++ b/packages/SystemUI/res/layout/global_actions_wrapped.xml
@@ -15,7 +15,7 @@
<!-- Global actions is right-aligned to be physically near power button -->
<LinearLayout
android:id="@android:id/list"
- android:layout_width="@dimen/global_actions_panel_width"
+ android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="top|right"
android:gravity="center"
@@ -26,7 +26,7 @@
<!-- For separated button-->
<FrameLayout
android:id="@+id/separated_button"
- android:layout_width="@dimen/global_actions_panel_width"
+ android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="top|right"
android:layout_marginTop="6dp"
diff --git a/packages/SystemUI/res/values-sw410dp/dimens.xml b/packages/SystemUI/res/values-sw410dp/dimens.xml
index 73d1857..fea1ef1 100644
--- a/packages/SystemUI/res/values-sw410dp/dimens.xml
+++ b/packages/SystemUI/res/values-sw410dp/dimens.xml
@@ -35,7 +35,7 @@
<dimen name="global_actions_grid_item_icon_width">24dp</dimen>
<dimen name="global_actions_grid_item_icon_height">24dp</dimen>
- <dimen name="global_actions_grid_item_icon_top_margin">14dp</dimen>
+ <dimen name="global_actions_grid_item_icon_top_margin">18dp</dimen>
<dimen name="global_actions_grid_item_icon_side_margin">24dp</dimen>
<dimen name="global_actions_grid_item_icon_bottom_margin">4dp</dimen>
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
index a4870d4..b1e2212 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
@@ -325,12 +325,11 @@
* Adds or updates a bubble associated with the provided notification entry.
*
* @param notif the notification associated with this bubble.
- * @param updatePosition whether this update should promote the bubble to the top of the stack.
*/
- public void updateBubble(NotificationEntry notif, boolean updatePosition) {
+ void updateBubble(NotificationEntry notif) {
if (mStackView != null && mBubbleData.getBubble(notif.key) != null) {
// It's an update
- mStackView.updateBubble(notif, updatePosition);
+ mStackView.updateBubble(notif);
} else {
if (mStackView == null) {
mStackView = new BubbleStackView(mContext, mBubbleData, mSurfaceSynchronizer);
@@ -403,7 +402,7 @@
return;
}
if (entry.isBubble() && mNotificationInterruptionStateProvider.shouldBubbleUp(entry)) {
- updateBubble(entry, true /* updatePosition */);
+ updateBubble(entry);
}
}
@@ -416,7 +415,7 @@
&& alertAgain(entry, entry.notification.getNotification())) {
entry.setShowInShadeWhenBubble(true);
entry.setBubbleDismissed(false); // updates come back as bubbles even if dismissed
- updateBubble(entry, true /* updatePosition */);
+ updateBubble(entry);
mStackView.updateDotVisibility(entry.key);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
index a4e1ad7..53e65e6 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
@@ -595,15 +595,13 @@
/**
* Updates a bubble in the stack.
- *
- * @param entry the entry to update in the stack.
- * @param updatePosition whether this bubble should be moved to top of the stack.
+ * @param entry the entry to update in the stack.
*/
- public void updateBubble(NotificationEntry entry, boolean updatePosition) {
+ public void updateBubble(NotificationEntry entry) {
Bubble b = mBubbleData.getBubble(entry.key);
mBubbleData.updateBubble(entry.key, entry);
- if (updatePosition && !mIsExpanded) {
+ if (!mIsExpanded) {
// If alerting it gets promoted to top of the stack.
if (mBubbleContainer.indexOfChild(b.iconView) != 0) {
mBubbleContainer.moveViewTo(b.iconView, 0);
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
index e22b24e..7a3f3be 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
@@ -505,6 +505,7 @@
}
TextView messageView = v.findViewById(R.id.message);
messageView.setTextColor(textColor);
+ messageView.setSelected(true); // necessary for marquee to work
ImageView icon = (ImageView) v.findViewById(R.id.icon);
icon.getDrawable().setTint(textColor);
return v;
@@ -1137,6 +1138,7 @@
ImageView icon = (ImageView) v.findViewById(R.id.icon);
TextView messageView = (TextView) v.findViewById(R.id.message);
+ messageView.setSelected(true); // necessary for marquee to work
TextView statusView = (TextView) v.findViewById(R.id.status);
final String status = getStatus();
@@ -1240,6 +1242,7 @@
if (messageView != null) {
messageView.setText(mMessageResId);
messageView.setEnabled(enabled);
+ messageView.setSelected(true); // necessary for marquee to work
}
boolean on = ((mState == State.On) || (mState == State.TurningOn));
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java
index a3db533..69d2e31 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java
@@ -284,9 +284,8 @@
@Override
public boolean isCaptionStreamOptedOut() {
- int currentValue = Settings.Secure.getInt(mContext.getContentResolver(),
- Settings.Secure.ODI_CAPTIONS_OPTED_OUT, 0);
- return currentValue == 1;
+ // TODO(b/129768185): Removing secure setting, to be replaced by sound event listener
+ return false;
}
public void getCaptionsComponentState(boolean fromTooltip) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
index 5e16721..20f539b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
@@ -160,7 +160,7 @@
@Test
public void testAddBubble() {
- mBubbleController.updateBubble(mRow.getEntry(), true /* updatePosition */);
+ mBubbleController.updateBubble(mRow.getEntry());
assertTrue(mBubbleController.hasBubbles());
verify(mBubbleStateChangeListener).onHasBubblesChanged(true);
@@ -169,13 +169,13 @@
@Test
public void testHasBubbles() {
assertFalse(mBubbleController.hasBubbles());
- mBubbleController.updateBubble(mRow.getEntry(), true /* updatePosition */);
+ mBubbleController.updateBubble(mRow.getEntry());
assertTrue(mBubbleController.hasBubbles());
}
@Test
public void testRemoveBubble() {
- mBubbleController.updateBubble(mRow.getEntry(), true /* updatePosition */);
+ mBubbleController.updateBubble(mRow.getEntry());
assertTrue(mBubbleController.hasBubbles());
verify(mBubbleStateChangeListener).onHasBubblesChanged(true);
@@ -189,8 +189,8 @@
@Test
public void testDismissStack() {
- mBubbleController.updateBubble(mRow.getEntry(), true /* updatePosition */);
- mBubbleController.updateBubble(mRow2.getEntry(), true /* updatePosition */);
+ mBubbleController.updateBubble(mRow.getEntry());
+ mBubbleController.updateBubble(mRow2.getEntry());
assertTrue(mBubbleController.hasBubbles());
mBubbleController.dismissStack(BubbleController.DISMISS_USER_GESTURE);
@@ -206,7 +206,7 @@
// Mark it as a bubble and add it explicitly
mEntryListener.onPendingEntryAdded(mRow.getEntry());
- mBubbleController.updateBubble(mRow.getEntry(), true /* updatePosition */);
+ mBubbleController.updateBubble(mRow.getEntry());
// We should have bubbles & their notifs should show in the shade
assertTrue(mBubbleController.hasBubbles());
@@ -235,8 +235,8 @@
// Mark it as a bubble and add it explicitly
mEntryListener.onPendingEntryAdded(mRow.getEntry());
mEntryListener.onPendingEntryAdded(mRow2.getEntry());
- mBubbleController.updateBubble(mRow.getEntry(), true /* updatePosition */);
- mBubbleController.updateBubble(mRow2.getEntry(), true /* updatePosition */);
+ mBubbleController.updateBubble(mRow.getEntry());
+ mBubbleController.updateBubble(mRow2.getEntry());
// We should have bubbles & their notifs should show in the shade
assertTrue(mBubbleController.hasBubbles());
@@ -272,7 +272,7 @@
public void testExpansionRemovesShowInShade() {
// Mark it as a bubble and add it explicitly
mEntryListener.onPendingEntryAdded(mRow.getEntry());
- mBubbleController.updateBubble(mRow.getEntry(), true /* updatePosition */);
+ mBubbleController.updateBubble(mRow.getEntry());
// We should have bubbles & their notifs should show in the shade
assertTrue(mBubbleController.hasBubbles());
@@ -293,8 +293,8 @@
// Mark it as a bubble and add it explicitly
mEntryListener.onPendingEntryAdded(mRow.getEntry());
mEntryListener.onPendingEntryAdded(mRow2.getEntry());
- mBubbleController.updateBubble(mRow.getEntry(), true /* updatePosition */);
- mBubbleController.updateBubble(mRow2.getEntry(), true /* updatePosition */);
+ mBubbleController.updateBubble(mRow.getEntry());
+ mBubbleController.updateBubble(mRow2.getEntry());
verify(mBubbleStateChangeListener).onHasBubblesChanged(true);
// Expand
@@ -333,7 +333,7 @@
// Add the auto expand bubble
mEntryListener.onPendingEntryAdded(mAutoExpandRow.getEntry());
- mBubbleController.updateBubble(mAutoExpandRow.getEntry(), true /* updatePosition */);
+ mBubbleController.updateBubble(mAutoExpandRow.getEntry());
// Expansion shouldn't change
verify(mBubbleExpandListener, never()).onBubbleExpandChanged(false /* expanded */,
@@ -371,7 +371,7 @@
// Add the auto expand bubble
mEntryListener.onPendingEntryAdded(mAutoExpandRow.getEntry());
- mBubbleController.updateBubble(mAutoExpandRow.getEntry(), true /* updatePosition */);
+ mBubbleController.updateBubble(mAutoExpandRow.getEntry());
// Expansion should change
verify(mBubbleExpandListener).onBubbleExpandChanged(true /* expanded */,
@@ -387,7 +387,7 @@
public void testSuppressNotif_FailsNotForeground() {
// Add the suppress notif bubble
mEntryListener.onPendingEntryAdded(mSuppressNotifRow.getEntry());
- mBubbleController.updateBubble(mSuppressNotifRow.getEntry(), true /* updatePosition */);
+ mBubbleController.updateBubble(mSuppressNotifRow.getEntry());
// Should show in shade because we weren't forground
assertTrue(mSuppressNotifRow.getEntry().showInShadeWhenBubble());
@@ -423,7 +423,7 @@
// Add the suppress notif bubble
mEntryListener.onPendingEntryAdded(mSuppressNotifRow.getEntry());
- mBubbleController.updateBubble(mSuppressNotifRow.getEntry(), true /* updatePosition */);
+ mBubbleController.updateBubble(mSuppressNotifRow.getEntry());
// Should NOT show in shade because we were foreground
assertFalse(mSuppressNotifRow.getEntry().showInShadeWhenBubble());
@@ -438,7 +438,7 @@
final String key = mRow.getEntry().key;
mEntryListener.onPendingEntryAdded(mRow.getEntry());
- mBubbleController.updateBubble(mRow.getEntry(), true /* updatePosition */);
+ mBubbleController.updateBubble(mRow.getEntry());
// Simulate notification cancellation.
mEntryListener.onEntryRemoved(mRow.getEntry(), null /* notificationVisibility (unused) */,
@@ -464,22 +464,22 @@
@Test
public void testDeleteIntent_removeBubble_aged() throws PendingIntent.CanceledException {
- mBubbleController.updateBubble(mRow.getEntry(), true /* updatePosition */);
+ mBubbleController.updateBubble(mRow.getEntry());
mBubbleController.removeBubble(mRow.getEntry().key, BubbleController.DISMISS_AGED);
verify(mDeleteIntent, never()).send();
}
@Test
public void testDeleteIntent_removeBubble_user() throws PendingIntent.CanceledException {
- mBubbleController.updateBubble(mRow.getEntry(), true /* updatePosition */);
+ mBubbleController.updateBubble(mRow.getEntry());
mBubbleController.removeBubble(mRow.getEntry().key, BubbleController.DISMISS_USER_GESTURE);
verify(mDeleteIntent, times(1)).send();
}
@Test
public void testDeleteIntent_dismissStack() throws PendingIntent.CanceledException {
- mBubbleController.updateBubble(mRow.getEntry(), true /* updatePosition */);
- mBubbleController.updateBubble(mRow2.getEntry(), true /* updatePosition */);
+ mBubbleController.updateBubble(mRow.getEntry());
+ mBubbleController.updateBubble(mRow2.getEntry());
mBubbleController.dismissStack(BubbleController.DISMISS_USER_GESTURE);
verify(mDeleteIntent, times(2)).send();
}
diff --git a/services/Android.bp b/services/Android.bp
index 567efac..b08d1a8 100644
--- a/services/Android.bp
+++ b/services/Android.bp
@@ -31,6 +31,7 @@
"services.print",
"services.restrictions",
"services.startop",
+ "services.systemcaptions",
"services.usage",
"services.usb",
"services.voiceinteraction",
diff --git a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java
index 9b02c4e..757c2dc 100644
--- a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java
+++ b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java
@@ -129,7 +129,8 @@
public ContentCaptureManagerService(@NonNull Context context) {
super(context, new FrameworkResourcesServiceNameResolver(context,
com.android.internal.R.string.config_defaultContentCaptureService),
- UserManager.DISALLOW_CONTENT_CAPTURE, /* refreshServiceOnPackageUpdate= */ false);
+ UserManager.DISALLOW_CONTENT_CAPTURE,
+ /*packageUpdatePolicy=*/ PACKAGE_UPDATE_POLICY_NO_REFRESH);
DeviceConfig.addOnPropertyChangedListener(DeviceConfig.NAMESPACE_CONTENT_CAPTURE,
ActivityThread.currentApplication().getMainExecutor(),
(namespace, key, value) -> onDeviceConfigChange(key, value));
diff --git a/services/core/java/com/android/server/PackageWatchdog.java b/services/core/java/com/android/server/PackageWatchdog.java
index feffe2f..0c681df 100644
--- a/services/core/java/com/android/server/PackageWatchdog.java
+++ b/services/core/java/com/android/server/PackageWatchdog.java
@@ -77,6 +77,7 @@
private static final String ATTR_VERSION = "version";
private static final String ATTR_NAME = "name";
private static final String ATTR_DURATION = "duration";
+ private static final String ATTR_EXPLICIT_HEALTH_CHECK_DURATION = "health-check-duration";
private static final String ATTR_PASSED_HEALTH_CHECK = "passed-health-check";
private static PackageWatchdog sPackageWatchdog;
@@ -95,20 +96,22 @@
private final ArrayMap<String, ObserverInternal> mAllObservers = new ArrayMap<>();
// File containing the XML data of monitored packages /data/system/package-watchdog.xml
private final AtomicFile mPolicyFile;
- // Runnable to prune monitored packages that have expired
- private final Runnable mPackageCleanup;
private final ExplicitHealthCheckController mHealthCheckController;
// Flag to control whether explicit health checks are supported or not
@GuardedBy("mLock")
private boolean mIsHealthCheckEnabled = true;
@GuardedBy("mLock")
private boolean mIsPackagesReady;
- // Last SystemClock#uptimeMillis a package clean up was executed.
- // 0 if mPackageCleanup not running.
- private long mUptimeAtLastRescheduleMs;
- // Duration a package cleanup was last scheduled for.
- // 0 if mPackageCleanup not running.
- private long mDurationAtLastReschedule;
+ // SystemClock#uptimeMillis when we last executed #pruneObservers.
+ // 0 if no prune is scheduled.
+ @GuardedBy("mLock")
+ private long mUptimeAtLastPruneMs;
+ // Duration in millis that the last prune was scheduled for.
+ // Used along with #mUptimeAtLastPruneMs after scheduling a prune to determine the remaining
+ // duration before #pruneObservers will be executed.
+ // 0 if no prune is scheduled.
+ @GuardedBy("mLock")
+ private long mDurationAtLastPrune;
private PackageWatchdog(Context context) {
// Needs to be constructed inline
@@ -129,7 +132,6 @@
mPolicyFile = policyFile;
mShortTaskHandler = shortTaskHandler;
mLongTaskHandler = longTaskHandler;
- mPackageCleanup = this::rescheduleCleanup;
mHealthCheckController = controller;
loadFromFile();
}
@@ -171,9 +173,9 @@
if (internalObserver != null) {
internalObserver.mRegisteredObserver = observer;
}
- if (mDurationAtLastReschedule == 0) {
- // Nothing running, schedule
- rescheduleCleanup();
+ if (mDurationAtLastPrune == 0) {
+ // Nothing running, prune
+ pruneAndSchedule();
}
}
}
@@ -208,6 +210,7 @@
List<MonitoredPackage> packages = new ArrayList<>();
for (int i = 0; i < packageNames.size(); i++) {
+ // Health checks not available yet so health check state will start INACTIVE
packages.add(new MonitoredPackage(packageNames.get(i), durationMs, false));
}
@@ -225,9 +228,9 @@
}
}
registerHealthObserver(observer);
- // Always reschedule because we may need to expire packages
- // earlier than we are already scheduled for
- rescheduleCleanup();
+ // Always prune because we may have received packges requiring an earlier
+ // schedule than we are currently scheduled for.
+ pruneAndSchedule();
Slog.i(TAG, "Syncing health check requests, observing packages " + packageNames);
syncRequestsAsync();
saveToFileAsync();
@@ -312,15 +315,18 @@
});
}
- // TODO(b/120598832): Optimize write? Maybe only write a separate smaller file?
+ // TODO(b/120598832): Optimize write? Maybe only write a separate smaller file? Also
+ // avoid holding lock?
// This currently adds about 7ms extra to shutdown thread
/** Writes the package information to file during shutdown. */
public void writeNow() {
- if (!mAllObservers.isEmpty()) {
- mLongTaskHandler.removeCallbacks(this::saveToFile);
- pruneObservers(SystemClock.uptimeMillis() - mUptimeAtLastRescheduleMs);
- saveToFile();
- Slog.i(TAG, "Last write to update package durations");
+ synchronized (mLock) {
+ if (!mAllObservers.isEmpty()) {
+ mLongTaskHandler.removeCallbacks(this::saveToFile);
+ pruneObservers(SystemClock.uptimeMillis() - mUptimeAtLastPruneMs);
+ saveToFile();
+ Slog.i(TAG, "Last write to update package durations");
+ }
}
}
@@ -450,9 +456,10 @@
private void onSupportedPackages(List<String> supportedPackages) {
boolean shouldUpdateFile = false;
+ boolean shouldPrune = false;
synchronized (mLock) {
- Slog.i(TAG, "Received supported packages " + supportedPackages);
+ Slog.d(TAG, "Received supported packages " + supportedPackages);
Iterator<ObserverInternal> oit = mAllObservers.values().iterator();
while (oit.hasNext()) {
ObserverInternal observer = oit.next();
@@ -461,12 +468,31 @@
while (pit.hasNext()) {
MonitoredPackage monitoredPackage = pit.next();
String packageName = monitoredPackage.mName;
- if (!monitoredPackage.mHasPassedHealthCheck
- && !supportedPackages.contains(packageName)) {
- // Hasn't passed health check but health check is not supported
- Slog.i(TAG, packageName + " does not support health checks, passing");
+ int healthCheckState = monitoredPackage.getHealthCheckState();
+
+ if (healthCheckState != MonitoredPackage.STATE_PASSED) {
+ // Have to update file, we will either transition state or reduce
+ // health check duration
shouldUpdateFile = true;
- monitoredPackage.mHasPassedHealthCheck = true;
+
+ if (supportedPackages.contains(packageName)) {
+ // Supports health check, transition to ACTIVE if not already.
+ // We need to prune packages earlier than already scheduled.
+ shouldPrune = true;
+
+ // TODO: Get healthCheckDuration from supportedPackages
+ long healthCheckDuration = monitoredPackage.mDurationMs;
+ monitoredPackage.mHealthCheckDurationMs = Math.min(healthCheckDuration,
+ monitoredPackage.mDurationMs);
+ Slog.i(TAG, packageName + " health check state is now: ACTIVE("
+ + monitoredPackage.mHealthCheckDurationMs + "ms)");
+ } else {
+ // Does not support health check, transistion to PASSED
+ monitoredPackage.mHasPassedHealthCheck = true;
+ Slog.i(TAG, packageName + " health check state is now: PASSED");
+ }
+ } else {
+ Slog.i(TAG, packageName + " does not support health check, state: PASSED");
}
}
}
@@ -475,6 +501,9 @@
if (shouldUpdateFile) {
saveToFileAsync();
}
+ if (shouldPrune) {
+ pruneAndSchedule();
+ }
}
private Set<String> getPackagesPendingHealthChecksLocked() {
@@ -496,59 +525,64 @@
return packages;
}
- /** Reschedules handler to prune expired packages from observers. */
- private void rescheduleCleanup() {
+ /** Executes {@link #pruneObservers} and schedules the next execution. */
+ private void pruneAndSchedule() {
synchronized (mLock) {
- long nextDurationToScheduleMs = getEarliestPackageExpiryLocked();
+ long nextDurationToScheduleMs = getNextPruneScheduleMillisLocked();
if (nextDurationToScheduleMs == Long.MAX_VALUE) {
- Slog.i(TAG, "No monitored packages, ending package cleanup");
- mDurationAtLastReschedule = 0;
- mUptimeAtLastRescheduleMs = 0;
+ Slog.i(TAG, "No monitored packages, ending prune");
+ mDurationAtLastPrune = 0;
+ mUptimeAtLastPruneMs = 0;
return;
}
long uptimeMs = SystemClock.uptimeMillis();
- // O if mPackageCleanup not running
- long elapsedDurationMs = mUptimeAtLastRescheduleMs == 0
- ? 0 : uptimeMs - mUptimeAtLastRescheduleMs;
- // Less than O if mPackageCleanup unexpectedly didn't run yet even though
- // and we are past the last duration scheduled to run
- long remainingDurationMs = mDurationAtLastReschedule - elapsedDurationMs;
- if (mUptimeAtLastRescheduleMs == 0
+ // O if not running
+ long elapsedDurationMs = mUptimeAtLastPruneMs == 0
+ ? 0 : uptimeMs - mUptimeAtLastPruneMs;
+ // Less than O if unexpectedly didn't run yet even though
+ // we are past the last duration scheduled to run
+ long remainingDurationMs = mDurationAtLastPrune - elapsedDurationMs;
+ if (mUptimeAtLastPruneMs == 0
|| remainingDurationMs <= 0
|| nextDurationToScheduleMs < remainingDurationMs) {
// First schedule or an earlier reschedule
pruneObservers(elapsedDurationMs);
- mShortTaskHandler.removeCallbacks(mPackageCleanup);
- mShortTaskHandler.postDelayed(mPackageCleanup, nextDurationToScheduleMs);
- mDurationAtLastReschedule = nextDurationToScheduleMs;
- mUptimeAtLastRescheduleMs = uptimeMs;
+ // We don't use Handler#hasCallbacks because we want to update the schedule delay
+ mShortTaskHandler.removeCallbacks(this::pruneAndSchedule);
+ mShortTaskHandler.postDelayed(this::pruneAndSchedule, nextDurationToScheduleMs);
+ mDurationAtLastPrune = nextDurationToScheduleMs;
+ mUptimeAtLastPruneMs = uptimeMs;
}
}
}
/**
- * Returns the earliest time a package should expire.
+ * Returns the next time in millis to schedule a prune.
+ *
* @returns Long#MAX_VALUE if there are no observed packages.
*/
- private long getEarliestPackageExpiryLocked() {
+ private long getNextPruneScheduleMillisLocked() {
long shortestDurationMs = Long.MAX_VALUE;
for (int oIndex = 0; oIndex < mAllObservers.size(); oIndex++) {
ArrayMap<String, MonitoredPackage> packages = mAllObservers.valueAt(oIndex).mPackages;
for (int pIndex = 0; pIndex < packages.size(); pIndex++) {
- long duration = packages.valueAt(pIndex).mDurationMs;
+ MonitoredPackage mp = packages.valueAt(pIndex);
+ long duration = Math.min(mp.mDurationMs, mp.mHealthCheckDurationMs);
if (duration < shortestDurationMs) {
shortestDurationMs = duration;
}
}
}
- Slog.v(TAG, "Earliest package time is " + shortestDurationMs);
+ Slog.i(TAG, "Next prune will be scheduled in " + shortestDurationMs + "ms");
return shortestDurationMs;
}
/**
* Removes {@code elapsedMs} milliseconds from all durations on monitored packages.
- * Discards expired packages and discards observers without any packages.
+ *
+ * <p> Prunes all observers with {@link ObserverInternal#prunePackages} and discards observers
+ * without any packages left.
*/
private void pruneObservers(long elapsedMs) {
if (elapsedMs == 0) {
@@ -559,8 +593,8 @@
Iterator<ObserverInternal> it = mAllObservers.values().iterator();
while (it.hasNext()) {
ObserverInternal observer = it.next();
- List<MonitoredPackage> failedPackages =
- observer.updateMonitoringDurations(elapsedMs);
+ Set<MonitoredPackage> failedPackages =
+ observer.prunePackages(elapsedMs);
if (!failedPackages.isEmpty()) {
onHealthCheckFailed(observer, failedPackages);
}
@@ -570,32 +604,34 @@
}
}
}
- Slog.i(TAG, "Syncing health check requests pruned packages");
+ Slog.i(TAG, "Syncing health check requests, pruned observers");
syncRequestsAsync();
saveToFileAsync();
}
private void onHealthCheckFailed(ObserverInternal observer,
- List<MonitoredPackage> failedPackages) {
+ Set<MonitoredPackage> failedPackages) {
mLongTaskHandler.post(() -> {
synchronized (mLock) {
PackageHealthObserver registeredObserver = observer.mRegisteredObserver;
if (registeredObserver != null) {
PackageManager pm = mContext.getPackageManager();
- for (int i = 0; i < failedPackages.size(); i++) {
- String packageName = failedPackages.get(i).mName;
+ Iterator<MonitoredPackage> it = failedPackages.iterator();
+ while (it.hasNext()) {
+ String failedPackage = it.next().mName;
long versionCode = 0;
- Slog.i(TAG, "Explicit health check failed for package " + packageName);
+ Slog.i(TAG, "Explicit health check failed for package " + failedPackage);
try {
versionCode = pm.getPackageInfo(
- packageName, 0 /* flags */).getLongVersionCode();
+ failedPackage, 0 /* flags */).getLongVersionCode();
} catch (PackageManager.NameNotFoundException e) {
Slog.w(TAG, "Explicit health check failed but could not find package "
- + packageName);
+ + failedPackage);
// TODO(b/120598832): Skip. We only continue to pass tests for now since
// the tests don't install any packages
}
- registeredObserver.execute(new VersionedPackage(packageName, versionCode));
+ registeredObserver.execute(
+ new VersionedPackage(failedPackage, versionCode));
}
}
}
@@ -670,34 +706,38 @@
}
private void saveToFileAsync() {
- // TODO(b/120598832): Use Handler#hasCallbacks instead of removing and posting
- mLongTaskHandler.removeCallbacks(this::saveToFile);
- mLongTaskHandler.post(this::saveToFile);
+ if (!mLongTaskHandler.hasCallbacks(this::saveToFile)) {
+ mLongTaskHandler.post(this::saveToFile);
+ }
}
/**
* Represents an observer monitoring a set of packages along with the failure thresholds for
* each package.
+ *
+ * <p> Note, the PackageWatchdog#mLock must always be held when reading or writing
+ * instances of this class.
*/
- static class ObserverInternal {
+ //TODO(b/120598832): Remove 'm' from non-private fields
+ private static class ObserverInternal {
public final String mName;
//TODO(b/120598832): Add getter for mPackages
- public final ArrayMap<String, MonitoredPackage> mPackages;
+ @GuardedBy("mLock")
+ public final ArrayMap<String, MonitoredPackage> mPackages = new ArrayMap<>();
@Nullable
+ @GuardedBy("mLock")
public PackageHealthObserver mRegisteredObserver;
ObserverInternal(String name, List<MonitoredPackage> packages) {
mName = name;
- mPackages = new ArrayMap<>();
updatePackages(packages);
}
/**
- * Writes important details to file. Doesn't persist any package failure thresholds.
- *
- * <p>Note that this method is <b>not</b> thread safe. It should only be called from
- * #saveToFile which runs on a single threaded handler.
+ * Writes important {@link MonitoredPackage} details for this observer to file.
+ * Does not persist any package failure thresholds.
*/
+ @GuardedBy("mLock")
public boolean write(XmlSerializer out) {
try {
out.startTag(null, TAG_OBSERVER);
@@ -707,6 +747,8 @@
out.startTag(null, TAG_PACKAGE);
out.attribute(null, ATTR_NAME, p.mName);
out.attribute(null, ATTR_DURATION, String.valueOf(p.mDurationMs));
+ out.attribute(null, ATTR_EXPLICIT_HEALTH_CHECK_DURATION,
+ String.valueOf(p.mHealthCheckDurationMs));
out.attribute(null, ATTR_PASSED_HEALTH_CHECK,
String.valueOf(p.mHasPassedHealthCheck));
out.endTag(null, TAG_PACKAGE);
@@ -719,56 +761,68 @@
}
}
+ @GuardedBy("mLock")
public void updatePackages(List<MonitoredPackage> packages) {
- synchronized (mName) {
- for (int pIndex = 0; pIndex < packages.size(); pIndex++) {
- MonitoredPackage p = packages.get(pIndex);
- mPackages.put(p.mName, p);
- }
+ for (int pIndex = 0; pIndex < packages.size(); pIndex++) {
+ MonitoredPackage p = packages.get(pIndex);
+ mPackages.put(p.mName, p);
}
}
/**
* Reduces the monitoring durations of all packages observed by this observer by
- * {@code elapsedMs}. If any duration is less than 0, the package is removed from
- * observation.
+ * {@code elapsedMs}. If any duration is less than 0, the package is removed from
+ * observation. If any health check duration is less than 0, the health check result
+ * is evaluated.
*
- * @returns a {@link List} of packages that were removed from the observer without explicit
+ * @returns a {@link Set} of packages that were removed from the observer without explicit
* health check passing, or an empty list if no package expired for which an explicit health
* check was still pending
*/
- public List<MonitoredPackage> updateMonitoringDurations(long elapsedMs) {
- List<MonitoredPackage> removedPackages = new ArrayList<>();
- synchronized (mName) {
- Iterator<MonitoredPackage> it = mPackages.values().iterator();
- while (it.hasNext()) {
- MonitoredPackage p = it.next();
- long newDuration = p.mDurationMs - elapsedMs;
- if (newDuration > 0) {
- p.mDurationMs = newDuration;
- } else {
- if (!p.mHasPassedHealthCheck) {
- removedPackages.add(p);
- }
- it.remove();
+ @GuardedBy("mLock")
+ private Set<MonitoredPackage> prunePackages(long elapsedMs) {
+ Set<MonitoredPackage> failedPackages = new ArraySet<>();
+ Iterator<MonitoredPackage> it = mPackages.values().iterator();
+ while (it.hasNext()) {
+ MonitoredPackage p = it.next();
+ int healthCheckState = p.getHealthCheckState();
+
+ // Handle health check timeouts
+ if (healthCheckState == MonitoredPackage.STATE_ACTIVE) {
+ // Only reduce duration if state is active
+ p.mHealthCheckDurationMs -= elapsedMs;
+ // Check duration after reducing duration
+ if (p.mHealthCheckDurationMs <= 0) {
+ failedPackages.add(p);
}
}
- return removedPackages;
+
+ // Handle package expiry
+ p.mDurationMs -= elapsedMs;
+ // Check duration after reducing duration
+ if (p.mDurationMs <= 0) {
+ if (healthCheckState == MonitoredPackage.STATE_INACTIVE) {
+ Slog.w(TAG, "Package " + p.mName
+ + " expiring without starting health check, failing");
+ failedPackages.add(p);
+ }
+ it.remove();
+ }
}
+ return failedPackages;
}
/**
* Increments failure counts of {@code packageName}.
* @returns {@code true} if failure threshold is exceeded, {@code false} otherwise
*/
+ @GuardedBy("mLock")
public boolean onPackageFailure(String packageName) {
- synchronized (mName) {
- MonitoredPackage p = mPackages.get(packageName);
- if (p != null) {
- return p.onFailure();
- }
- return false;
+ MonitoredPackage p = mPackages.get(packageName);
+ if (p != null) {
+ return p.onFailure();
}
+ return false;
}
/**
@@ -796,11 +850,14 @@
String packageName = parser.getAttributeValue(null, ATTR_NAME);
long duration = Long.parseLong(
parser.getAttributeValue(null, ATTR_DURATION));
+ long healthCheckDuration = Long.parseLong(
+ parser.getAttributeValue(null,
+ ATTR_EXPLICIT_HEALTH_CHECK_DURATION));
boolean hasPassedHealthCheck = Boolean.parseBoolean(
parser.getAttributeValue(null, ATTR_PASSED_HEALTH_CHECK));
if (!TextUtils.isEmpty(packageName)) {
packages.add(new MonitoredPackage(packageName, duration,
- hasPassedHealthCheck));
+ healthCheckDuration, hasPassedHealthCheck));
}
} catch (NumberFormatException e) {
Slog.wtf(TAG, "Skipping package for observer " + observerName, e);
@@ -819,21 +876,50 @@
}
}
- /** Represents a package along with the time it should be monitored for. */
- static class MonitoredPackage {
+ /**
+ * Represents a package along with the time it should be monitored for.
+ *
+ * <p> Note, the PackageWatchdog#mLock must always be held when reading or writing
+ * instances of this class.
+ */
+ //TODO(b/120598832): Remove 'm' from non-private fields
+ private static class MonitoredPackage {
+ // Health check states
+ // mName has not passed health check but has requested a health check
+ public static int STATE_ACTIVE = 0;
+ // mName has not passed health check and has not requested a health check
+ public static int STATE_INACTIVE = 1;
+ // mName has passed health check
+ public static int STATE_PASSED = 2;
+
public final String mName;
// Whether an explicit health check has passed
+ @GuardedBy("mLock")
public boolean mHasPassedHealthCheck;
// System uptime duration to monitor package
+ @GuardedBy("mLock")
public long mDurationMs;
+ // System uptime duration to check the result of an explicit health check
+ // Initially, MAX_VALUE until we get a value from the health check service
+ // and request health checks.
+ @GuardedBy("mLock")
+ public long mHealthCheckDurationMs = Long.MAX_VALUE;
// System uptime of first package failure
+ @GuardedBy("mLock")
private long mUptimeStartMs;
// Number of failures since mUptimeStartMs
+ @GuardedBy("mLock")
private int mFailures;
MonitoredPackage(String name, long durationMs, boolean hasPassedHealthCheck) {
+ this(name, durationMs, Long.MAX_VALUE, hasPassedHealthCheck);
+ }
+
+ MonitoredPackage(String name, long durationMs, long healthCheckDurationMs,
+ boolean hasPassedHealthCheck) {
mName = name;
mDurationMs = durationMs;
+ mHealthCheckDurationMs = healthCheckDurationMs;
mHasPassedHealthCheck = hasPassedHealthCheck;
}
@@ -842,7 +928,8 @@
*
* @return {@code true} if failure count exceeds a threshold, {@code false} otherwise
*/
- public synchronized boolean onFailure() {
+ @GuardedBy("mLock")
+ public boolean onFailure() {
final long now = SystemClock.uptimeMillis();
final long duration = now - mUptimeStartMs;
if (duration > TRIGGER_DURATION_MS) {
@@ -860,5 +947,20 @@
}
return failed;
}
+
+ /**
+ * Returns any of the health check states of {@link #STATE_ACTIVE},
+ * {@link #STATE_INACTIVE} or {@link #STATE_PASSED}
+ */
+ @GuardedBy("mLock")
+ public int getHealthCheckState() {
+ if (mHasPassedHealthCheck) {
+ return STATE_PASSED;
+ } else if (mHealthCheckDurationMs == Long.MAX_VALUE) {
+ return STATE_INACTIVE;
+ } else {
+ return STATE_ACTIVE;
+ }
+ }
}
}
diff --git a/services/core/java/com/android/server/infra/AbstractMasterSystemService.java b/services/core/java/com/android/server/infra/AbstractMasterSystemService.java
index 098b0e9..9782f30 100644
--- a/services/core/java/com/android/server/infra/AbstractMasterSystemService.java
+++ b/services/core/java/com/android/server/infra/AbstractMasterSystemService.java
@@ -15,6 +15,7 @@
*/
package com.android.server.infra;
+import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
@@ -45,6 +46,8 @@
import com.android.server.SystemService;
import java.io.PrintWriter;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.List;
/**
@@ -75,6 +78,30 @@
public abstract class AbstractMasterSystemService<M extends AbstractMasterSystemService<M, S>,
S extends AbstractPerUserSystemService<S, M>> extends SystemService {
+ /** On a package update, does not refresh the per-user service in the cache. */
+ public static final int PACKAGE_UPDATE_POLICY_NO_REFRESH = 0;
+
+ /**
+ * On a package update, removes any existing per-user services in the cache.
+ *
+ * <p>This does not immediately recreate these services. It is assumed they will be recreated
+ * for the next user request.
+ */
+ public static final int PACKAGE_UPDATE_POLICY_REFRESH_LAZY = 1;
+
+ /**
+ * On a package update, removes and recreates any existing per-user services in the cache.
+ */
+ public static final int PACKAGE_UPDATE_POLICY_REFRESH_EAGER = 2;
+
+ @IntDef(flag = true, prefix = { "PACKAGE_UPDATE_POLICY_" }, value = {
+ PACKAGE_UPDATE_POLICY_NO_REFRESH,
+ PACKAGE_UPDATE_POLICY_REFRESH_LAZY,
+ PACKAGE_UPDATE_POLICY_REFRESH_EAGER
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface PackageUpdatePolicy {}
+
/**
* Log tag
*/
@@ -127,8 +154,11 @@
/**
* Whether the per-user service should be removed from the cache when its apk is updated.
+ *
+ * <p>One of {@link #PACKAGE_UPDATE_POLICY_NO_REFRESH},
+ * {@link #PACKAGE_UPDATE_POLICY_REFRESH_LAZY} or {@link #PACKAGE_UPDATE_POLICY_REFRESH_EAGER}.
*/
- private final boolean mRefreshServiceOnPackageUpdate;
+ private final @PackageUpdatePolicy int mPackageUpdatePolicy;
/**
* Name of the service packages whose APK are being updated, keyed by user id.
@@ -154,7 +184,7 @@
@Nullable ServiceNameResolver serviceNameResolver,
@Nullable String disallowProperty) {
this(context, serviceNameResolver, disallowProperty,
- /* refreshServiceOnPackageUpdate=*/ true);
+ /*packageUpdatePolicy=*/ PACKAGE_UPDATE_POLICY_REFRESH_LAZY);
}
/**
@@ -167,17 +197,19 @@
* @param disallowProperty when not {@code null}, defines a {@link UserManager} restriction that
* disables the service. <b>NOTE: </b> you'll also need to add it to
* {@code UserRestrictionsUtils.USER_RESTRICTIONS}.
- * @param refreshServiceOnPackageUpdate when {@code true}, the
- * {@link AbstractPerUserSystemService} is removed from the cache (and re-added) when the
- * service package is updated; when {@code false}, the service is untouched during the
- * update.
+ * @param packageUpdatePolicy when {@link #PACKAGE_UPDATE_POLICY_REFRESH_LAZY}, the
+ * {@link AbstractPerUserSystemService} is removed from the cache when the service
+ * package is updated; when {@link #PACKAGE_UPDATE_POLICY_REFRESH_EAGER}, the
+ * {@link AbstractPerUserSystemService} is removed from the cache and immediately
+ * re-added when the service package is updated; when
+ * {@link #PACKAGE_UPDATE_POLICY_NO_REFRESH}, the service is untouched during the update.
*/
protected AbstractMasterSystemService(@NonNull Context context,
@Nullable ServiceNameResolver serviceNameResolver,
- @Nullable String disallowProperty, boolean refreshServiceOnPackageUpdate) {
+ @Nullable String disallowProperty, @PackageUpdatePolicy int packageUpdatePolicy) {
super(context);
- mRefreshServiceOnPackageUpdate = refreshServiceOnPackageUpdate;
+ mPackageUpdatePolicy = packageUpdatePolicy;
mServiceNameResolver = serviceNameResolver;
if (mServiceNameResolver != null) {
@@ -645,7 +677,7 @@
final int size = mServicesCache.size();
pw.print(prefix); pw.print("Debug: "); pw.print(realDebug);
pw.print(" Verbose: "); pw.println(realVerbose);
- pw.print("Refresh on package update: "); pw.println(mRefreshServiceOnPackageUpdate);
+ pw.print("Refresh on package update: "); pw.println(mPackageUpdatePolicy);
if (mUpdatingPackageNames != null) {
pw.print("Packages being updated: "); pw.println(mUpdatingPackageNames);
}
@@ -701,12 +733,21 @@
}
mUpdatingPackageNames.put(userId, packageName);
onServicePackageUpdatingLocked(userId);
- if (mRefreshServiceOnPackageUpdate) {
+ if (mPackageUpdatePolicy != PACKAGE_UPDATE_POLICY_NO_REFRESH) {
if (debug) {
- Slog.d(mTag, "Removing service for user " + userId + " because package "
- + activePackageName + " is being updated");
+ Slog.d(mTag, "Removing service for user " + userId
+ + " because package " + activePackageName
+ + " is being updated");
}
removeCachedServiceLocked(userId);
+
+ if (mPackageUpdatePolicy == PACKAGE_UPDATE_POLICY_REFRESH_EAGER) {
+ if (debug) {
+ Slog.d(mTag, "Eagerly recreating service for user "
+ + userId);
+ }
+ getServiceForUserLocked(userId);
+ }
} else {
if (debug) {
Slog.d(mTag, "Holding service for user " + userId + " while package "
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 4824d7b..0cd730b 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -256,6 +256,8 @@
"com.android.server.autofill.AutofillManagerService";
private static final String CONTENT_CAPTURE_MANAGER_SERVICE_CLASS =
"com.android.server.contentcapture.ContentCaptureManagerService";
+ private static final String SYSTEM_CAPTIONS_MANAGER_SERVICE_CLASS =
+ "com.android.server.systemcaptions.SystemCaptionsManagerService";
private static final String TIME_ZONE_RULES_MANAGER_SERVICE_CLASS =
"com.android.server.timezone.RulesManagerService$Lifecycle";
private static final String IOT_SERVICE_CLASS =
@@ -1234,6 +1236,8 @@
startContentCaptureService(context);
startAttentionService(context);
+ startSystemCaptionsManagerService(context);
+
// App prediction manager service
traceBeginAndSlog("StartAppPredictionService");
mSystemServiceManager.startService(APP_PREDICTION_MANAGER_SERVICE_CLASS);
@@ -2232,6 +2236,19 @@
}, BOOT_TIMINGS_TRACE_LOG);
}
+ private void startSystemCaptionsManagerService(@NonNull Context context) {
+ String serviceName = context.getString(
+ com.android.internal.R.string.config_defaultSystemCaptionsManagerService);
+ if (TextUtils.isEmpty(serviceName)) {
+ Slog.d(TAG, "SystemCaptionsManagerService disabled because resource is not overlaid");
+ return;
+ }
+
+ traceBeginAndSlog("StartSystemCaptionsManagerService");
+ mSystemServiceManager.startService(SYSTEM_CAPTIONS_MANAGER_SERVICE_CLASS);
+ traceEnd();
+ }
+
private void startContentCaptureService(@NonNull Context context) {
// First check if it was explicitly enabled by DeviceConfig
boolean explicitlyEnabled = false;
@@ -2280,7 +2297,7 @@
traceEnd();
}
- static final void startSystemUi(Context context, WindowManagerService windowManager) {
+ private static void startSystemUi(Context context, WindowManagerService windowManager) {
Intent intent = new Intent();
intent.setComponent(new ComponentName("com.android.systemui",
"com.android.systemui.SystemUIService"));
diff --git a/services/net/java/android/net/netlink/NetlinkSocket.java b/services/net/java/android/net/netlink/NetlinkSocket.java
index 4240d24..7311fc5 100644
--- a/services/net/java/android/net/netlink/NetlinkSocket.java
+++ b/services/net/java/android/net/netlink/NetlinkSocket.java
@@ -30,6 +30,7 @@
import android.net.util.SocketUtils;
import android.system.ErrnoException;
import android.system.Os;
+import android.system.StructTimeval;
import android.util.Log;
import java.io.FileDescriptor;
@@ -128,7 +129,7 @@
throws ErrnoException, IllegalArgumentException, InterruptedIOException {
checkTimeout(timeoutMs);
- SocketUtils.setSocketTimeValueOption(fd, SOL_SOCKET, SO_RCVTIMEO, timeoutMs);
+ Os.setsockoptTimeval(fd, SOL_SOCKET, SO_RCVTIMEO, StructTimeval.fromMillis(timeoutMs));
ByteBuffer byteBuffer = ByteBuffer.allocate(bufsize);
int length = Os.read(fd, byteBuffer);
@@ -151,7 +152,7 @@
FileDescriptor fd, byte[] bytes, int offset, int count, long timeoutMs)
throws ErrnoException, IllegalArgumentException, InterruptedIOException {
checkTimeout(timeoutMs);
- SocketUtils.setSocketTimeValueOption(fd, SOL_SOCKET, SO_SNDTIMEO, timeoutMs);
+ Os.setsockoptTimeval(fd, SOL_SOCKET, SO_SNDTIMEO, StructTimeval.fromMillis(timeoutMs));
return Os.write(fd, bytes, offset, count);
}
}
diff --git a/services/systemcaptions/Android.bp b/services/systemcaptions/Android.bp
new file mode 100644
index 0000000..4e190b6
--- /dev/null
+++ b/services/systemcaptions/Android.bp
@@ -0,0 +1,5 @@
+java_library_static {
+ name: "services.systemcaptions",
+ srcs: ["java/**/*.java"],
+ libs: ["services.core"],
+}
diff --git a/services/systemcaptions/java/com/android/server/systemcaptions/RemoteSystemCaptionsManagerService.java b/services/systemcaptions/java/com/android/server/systemcaptions/RemoteSystemCaptionsManagerService.java
new file mode 100644
index 0000000..5480b6c
--- /dev/null
+++ b/services/systemcaptions/java/com/android/server/systemcaptions/RemoteSystemCaptionsManagerService.java
@@ -0,0 +1,164 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.systemcaptions;
+
+import android.annotation.Nullable;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.UserHandle;
+import android.util.Slog;
+
+import com.android.internal.annotations.GuardedBy;
+
+/** Manages the connection to the remote system captions manager service. */
+final class RemoteSystemCaptionsManagerService {
+
+ private static final String TAG = RemoteSystemCaptionsManagerService.class.getSimpleName();
+
+ private static final String SERVICE_INTERFACE =
+ "android.service.systemcaptions.SystemCaptionsManagerService";
+
+ private final Object mLock = new Object();
+
+ private final Context mContext;
+ private final Intent mIntent;
+ private final ComponentName mComponentName;
+ private final int mUserId;
+ private final boolean mVerbose;
+ private final Handler mHandler;
+
+ private final RemoteServiceConnection mServiceConnection = new RemoteServiceConnection();
+
+ @GuardedBy("mLock")
+ @Nullable private IBinder mService;
+
+ @GuardedBy("mLock")
+ private boolean mBinding = false;
+
+ @GuardedBy("mLock")
+ private boolean mDestroyed = false;
+
+ RemoteSystemCaptionsManagerService(
+ Context context, ComponentName componentName, int userId, boolean verbose) {
+ mContext = context;
+ mComponentName = componentName;
+ mUserId = userId;
+ mVerbose = verbose;
+ mIntent = new Intent(SERVICE_INTERFACE).setComponent(componentName);
+ mHandler = new Handler(Looper.getMainLooper());
+ }
+
+ void initialize() {
+ if (mVerbose) {
+ Slog.v(TAG, "initialize()");
+ }
+ ensureBound();
+ }
+
+ void destroy() {
+ if (mVerbose) {
+ Slog.v(TAG, "destroy()");
+ }
+
+ synchronized (mLock) {
+ if (mDestroyed) {
+ if (mVerbose) {
+ Slog.v(TAG, "destroy(): Already destroyed");
+ }
+ return;
+ }
+ mDestroyed = true;
+ ensureUnboundLocked();
+ }
+ }
+
+ boolean isDestroyed() {
+ synchronized (mLock) {
+ return mDestroyed;
+ }
+ }
+
+ private void ensureBound() {
+ synchronized (mLock) {
+ if (mService != null || mBinding) {
+ return;
+ }
+
+ if (mVerbose) {
+ Slog.v(TAG, "ensureBound(): binding");
+ }
+ mBinding = true;
+
+ int flags = Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE;
+ boolean willBind = mContext.bindServiceAsUser(mIntent, mServiceConnection, flags,
+ mHandler, new UserHandle(mUserId));
+ if (!willBind) {
+ Slog.w(TAG, "Could not bind to " + mIntent + " with flags " + flags);
+ mBinding = false;
+ mService = null;
+ }
+ }
+ }
+
+ @GuardedBy("mLock")
+ private void ensureUnboundLocked() {
+ if (mService == null && !mBinding) {
+ return;
+ }
+
+ mBinding = false;
+ mService = null;
+
+ if (mVerbose) {
+ Slog.v(TAG, "ensureUnbound(): unbinding");
+ }
+ mContext.unbindService(mServiceConnection);
+ }
+
+ private class RemoteServiceConnection implements ServiceConnection {
+ @Override
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ synchronized (mLock) {
+ if (mVerbose) {
+ Slog.v(TAG, "onServiceConnected()");
+ }
+ if (mDestroyed || !mBinding) {
+ Slog.wtf(TAG, "onServiceConnected() dispatched after unbindService");
+ return;
+ }
+ mBinding = false;
+ mService = service;
+ }
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName name) {
+ synchronized (mLock) {
+ if (mVerbose) {
+ Slog.v(TAG, "onServiceDisconnected()");
+ }
+ mBinding = true;
+ mService = null;
+ }
+ }
+ }
+}
diff --git a/services/systemcaptions/java/com/android/server/systemcaptions/SystemCaptionsManagerPerUserService.java b/services/systemcaptions/java/com/android/server/systemcaptions/SystemCaptionsManagerPerUserService.java
new file mode 100644
index 0000000..b503670
--- /dev/null
+++ b/services/systemcaptions/java/com/android/server/systemcaptions/SystemCaptionsManagerPerUserService.java
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.systemcaptions;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.UserIdInt;
+import android.app.AppGlobals;
+import android.content.ComponentName;
+import android.content.pm.PackageManager;
+import android.content.pm.ServiceInfo;
+import android.os.RemoteException;
+import android.util.Slog;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.server.infra.AbstractPerUserSystemService;
+
+/** Manages the captions manager service on a per-user basis. */
+final class SystemCaptionsManagerPerUserService extends
+ AbstractPerUserSystemService<SystemCaptionsManagerPerUserService,
+ SystemCaptionsManagerService> {
+
+ private static final String TAG = SystemCaptionsManagerPerUserService.class.getSimpleName();
+
+ @Nullable
+ @GuardedBy("mLock")
+ private RemoteSystemCaptionsManagerService mRemoteService;
+
+ SystemCaptionsManagerPerUserService(
+ @NonNull SystemCaptionsManagerService master,
+ @NonNull Object lock, boolean disabled, @UserIdInt int userId) {
+ super(master, lock, userId);
+ }
+
+ @Override
+ @NonNull
+ protected ServiceInfo newServiceInfoLocked(
+ @SuppressWarnings("unused") @NonNull ComponentName serviceComponent)
+ throws PackageManager.NameNotFoundException {
+ try {
+ return AppGlobals.getPackageManager().getServiceInfo(serviceComponent,
+ PackageManager.GET_META_DATA, mUserId);
+ } catch (RemoteException e) {
+ throw new PackageManager.NameNotFoundException(
+ "Could not get service for " + serviceComponent);
+ }
+ }
+
+ @GuardedBy("mLock")
+ void initializeLocked() {
+ if (mMaster.verbose) {
+ Slog.v(TAG, "initialize()");
+ }
+
+ RemoteSystemCaptionsManagerService service = getRemoteServiceLocked();
+ if (service == null && mMaster.verbose) {
+ Slog.v(TAG, "initialize(): Failed to init remote server");
+ }
+ }
+
+ @GuardedBy("mLock")
+ void destroyLocked() {
+ if (mMaster.verbose) {
+ Slog.v(TAG, "destroyLocked()");
+ }
+
+ if (mRemoteService != null) {
+ mRemoteService.destroy();
+ mRemoteService = null;
+ }
+ }
+
+ @GuardedBy("mLock")
+ @Nullable
+ private RemoteSystemCaptionsManagerService getRemoteServiceLocked() {
+ if (mRemoteService == null) {
+ String serviceName = getComponentNameLocked();
+ if (serviceName == null) {
+ if (mMaster.verbose) {
+ Slog.v(TAG, "getRemoteServiceLocked(): Not set");
+ }
+ return null;
+ }
+
+ ComponentName serviceComponent = ComponentName.unflattenFromString(serviceName);
+ mRemoteService = new RemoteSystemCaptionsManagerService(
+ getContext(),
+ serviceComponent,
+ mUserId,
+ mMaster.verbose);
+ if (mMaster.verbose) {
+ Slog.v(TAG, "getRemoteServiceLocked(): initialize for user " + mUserId);
+ }
+ mRemoteService.initialize();
+ }
+
+ return mRemoteService;
+ }
+}
diff --git a/services/systemcaptions/java/com/android/server/systemcaptions/SystemCaptionsManagerService.java b/services/systemcaptions/java/com/android/server/systemcaptions/SystemCaptionsManagerService.java
new file mode 100644
index 0000000..27a116c
--- /dev/null
+++ b/services/systemcaptions/java/com/android/server/systemcaptions/SystemCaptionsManagerService.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.systemcaptions;
+
+import android.annotation.NonNull;
+import android.annotation.UserIdInt;
+import android.content.Context;
+
+import com.android.server.infra.AbstractMasterSystemService;
+import com.android.server.infra.FrameworkResourcesServiceNameResolver;
+
+/** A system service to bind to a remote system captions manager service. */
+public final class SystemCaptionsManagerService extends
+ AbstractMasterSystemService<SystemCaptionsManagerService,
+ SystemCaptionsManagerPerUserService> {
+
+ public SystemCaptionsManagerService(@NonNull Context context) {
+ super(context,
+ new FrameworkResourcesServiceNameResolver(
+ context,
+ com.android.internal.R.string.config_defaultSystemCaptionsManagerService),
+ /*disallowProperty=*/ null,
+ /*packageUpdatePolicy=*/ PACKAGE_UPDATE_POLICY_REFRESH_EAGER);
+ }
+
+ @Override
+ public void onStart() {
+ // Do nothing. This service does not publish any local or system services.
+ }
+
+ @Override
+ protected SystemCaptionsManagerPerUserService newServiceLocked(
+ @UserIdInt int resolvedUserId, boolean disabled) {
+ SystemCaptionsManagerPerUserService perUserService =
+ new SystemCaptionsManagerPerUserService(this, mLock, disabled, resolvedUserId);
+ perUserService.initializeLocked();
+ return perUserService;
+ }
+
+ @Override
+ protected void onServiceRemoved(
+ SystemCaptionsManagerPerUserService service, @UserIdInt int userId) {
+ synchronized (mLock) {
+ service.destroyLocked();
+ }
+ }
+}
diff --git a/telephony/java/android/telephony/ServiceState.java b/telephony/java/android/telephony/ServiceState.java
index d2c0705..8c92e84 100644
--- a/telephony/java/android/telephony/ServiceState.java
+++ b/telephony/java/android/telephony/ServiceState.java
@@ -1045,6 +1045,7 @@
mIsEmergencyOnly = false;
mLteEarfcnRsrpBoost = 0;
mNrFrequencyRange = FREQUENCY_RANGE_UNKNOWN;
+ mNetworkRegistrationInfos.clear();
addNetworkRegistrationInfo(new NetworkRegistrationInfo.Builder()
.setDomain(NetworkRegistrationInfo.DOMAIN_CS)
.setTransportType(AccessNetworkConstants.TRANSPORT_TYPE_WWAN)
diff --git a/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java b/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java
index 33bb4cc..b308982 100644
--- a/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java
+++ b/tests/PackageWatchdog/src/com/android/server/PackageWatchdogTest.java
@@ -661,8 +661,10 @@
if (mIsEnabled) {
packages.retainAll(mSupportedPackages);
mRequestedPackages.addAll(packages);
+ mSupportedConsumer.accept(mSupportedPackages);
+ } else {
+ mSupportedConsumer.accept(Collections.emptyList());
}
- mSupportedConsumer.accept(mSupportedPackages);
}
public void setSupportedPackages(List<String> packages) {