diff options
44 files changed, 1226 insertions, 174 deletions
diff --git a/Android.bp b/Android.bp index d89b5013df12..7ce8b31e42c2 100644 --- a/Android.bp +++ b/Android.bp @@ -375,6 +375,7 @@ java_defaults { "core/java/android/view/IApplicationToken.aidl", "core/java/android/view/IAppTransitionAnimationSpecsFuture.aidl", "core/java/android/view/IDockedStackListener.aidl", + "core/java/android/view/IDisplayFoldListener.aidl", "core/java/android/view/IGraphicsStats.aidl", "core/java/android/view/IGraphicsStatsCallback.aidl", "core/java/android/view/IInputFilter.aidl", diff --git a/api/system-current.txt b/api/system-current.txt index f405eaf54c65..82bafab9aacc 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -4095,6 +4095,7 @@ package android.net { public final class IpPrefix implements android.os.Parcelable { ctor public IpPrefix(java.net.InetAddress, int); + ctor public IpPrefix(String); } public final class IpSecManager { @@ -4117,6 +4118,7 @@ package android.net { ctor public LinkAddress(java.net.InetAddress, int, int, int); ctor public LinkAddress(java.net.InetAddress, int); ctor public LinkAddress(String); + ctor public LinkAddress(String, int, int); method public boolean isGlobalPreferred(); method public boolean isIPv4(); method public boolean isIPv6(); @@ -4127,6 +4129,7 @@ package android.net { ctor public LinkProperties(); ctor public LinkProperties(android.net.LinkProperties); method public boolean addDnsServer(java.net.InetAddress); + method public boolean addLinkAddress(android.net.LinkAddress); method public boolean addRoute(android.net.RouteInfo); method public void clear(); method @Nullable public android.net.IpPrefix getNat64Prefix(); @@ -4141,6 +4144,7 @@ package android.net { method public boolean isProvisioned(); method public boolean isReachable(java.net.InetAddress); method public boolean removeDnsServer(java.net.InetAddress); + method public boolean removeLinkAddress(android.net.LinkAddress); method public boolean removeRoute(android.net.RouteInfo); method public void setDnsServers(java.util.Collection<java.net.InetAddress>); method public void setDomains(String); @@ -4157,6 +4161,7 @@ package android.net { } public class Network implements android.os.Parcelable { + ctor public Network(android.net.Network); method public android.net.Network getPrivateDnsBypassingCopy(); } @@ -4292,8 +4297,8 @@ package android.net.apf { public class ApfCapabilities { ctor public ApfCapabilities(int, int, int); - method public boolean getApfDrop8023Frames(android.content.Context); - method public int[] getApfEthTypeBlackList(android.content.Context); + method public static boolean getApfDrop8023Frames(android.content.Context); + method public static int[] getApfEthTypeBlackList(android.content.Context); method public boolean hasDataAccess(); field public final int apfPacketFormat; field public final int apfVersionSupported; @@ -4490,11 +4495,19 @@ package android.net.metrics { package android.net.util { public class SocketUtils { + method public static void addArpEntry(java.net.Inet4Address, android.net.MacAddress, String, java.io.FileDescriptor) throws java.io.IOException; + method public static void attachControlPacketFilter(java.io.FileDescriptor, int) throws java.net.SocketException; + method public static void attachDhcpFilter(java.io.FileDescriptor) throws java.net.SocketException; + method public static void attachRaFilter(java.io.FileDescriptor, int) throws java.net.SocketException; + method public static void bindSocket(java.io.FileDescriptor, java.net.SocketAddress) throws android.system.ErrnoException, java.net.SocketException; method public static void bindSocketToInterface(java.io.FileDescriptor, String) throws android.system.ErrnoException; method public static void closeSocket(java.io.FileDescriptor) throws java.io.IOException; + method public static void connectSocket(java.io.FileDescriptor, java.net.SocketAddress) throws android.system.ErrnoException, java.net.SocketException; method public static java.net.SocketAddress makeNetlinkSocketAddress(int, int); method public static java.net.SocketAddress makePacketSocketAddress(short, int); method public static java.net.SocketAddress makePacketSocketAddress(int, byte[]); + method public static void sendTo(java.io.FileDescriptor, byte[], int, int, int, java.net.SocketAddress) throws android.system.ErrnoException, java.net.SocketException; + method public static void setSocketTimeValueOption(java.io.FileDescriptor, int, int, long) throws android.system.ErrnoException; } } diff --git a/api/test-current.txt b/api/test-current.txt index 2295a21bf109..9c27535664fe 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -812,6 +812,7 @@ package android.net { public final class IpPrefix implements android.os.Parcelable { ctor public IpPrefix(java.net.InetAddress, int); + ctor public IpPrefix(String); } public final class IpSecManager { @@ -820,6 +821,9 @@ package android.net { public class LinkAddress implements android.os.Parcelable { ctor public LinkAddress(java.net.InetAddress, int, int, int); + ctor public LinkAddress(java.net.InetAddress, int); + ctor public LinkAddress(String); + ctor public LinkAddress(String, int, int); method public boolean isGlobalPreferred(); method public boolean isIPv4(); method public boolean isIPv6(); @@ -829,6 +833,7 @@ package android.net { public final class LinkProperties implements android.os.Parcelable { ctor public LinkProperties(android.net.LinkProperties); method public boolean addDnsServer(java.net.InetAddress); + method public boolean addLinkAddress(android.net.LinkAddress); method @Nullable public android.net.IpPrefix getNat64Prefix(); method public java.util.List<java.net.InetAddress> getPcscfServers(); method public String getTcpBufferSizes(); @@ -841,6 +846,7 @@ package android.net { method public boolean isProvisioned(); method public boolean isReachable(java.net.InetAddress); method public boolean removeDnsServer(java.net.InetAddress); + method public boolean removeLinkAddress(android.net.LinkAddress); method public boolean removeRoute(android.net.RouteInfo); method public void setNat64Prefix(android.net.IpPrefix); method public void setPcscfServers(java.util.Collection<java.net.InetAddress>); @@ -851,6 +857,7 @@ package android.net { } public class Network implements android.os.Parcelable { + ctor public Network(android.net.Network); method public android.net.Network getPrivateDnsBypassingCopy(); } @@ -902,8 +909,8 @@ package android.net.apf { public class ApfCapabilities { ctor public ApfCapabilities(int, int, int); - method public boolean getApfDrop8023Frames(android.content.Context); - method public int[] getApfEthTypeBlackList(android.content.Context); + method public static boolean getApfDrop8023Frames(android.content.Context); + method public static int[] getApfEthTypeBlackList(android.content.Context); method public boolean hasDataAccess(); field public final int apfPacketFormat; field public final int apfVersionSupported; @@ -1100,11 +1107,19 @@ package android.net.metrics { package android.net.util { public class SocketUtils { + method public static void addArpEntry(java.net.Inet4Address, android.net.MacAddress, String, java.io.FileDescriptor) throws java.io.IOException; + method public static void attachControlPacketFilter(java.io.FileDescriptor, int) throws java.net.SocketException; + method public static void attachDhcpFilter(java.io.FileDescriptor) throws java.net.SocketException; + method public static void attachRaFilter(java.io.FileDescriptor, int) throws java.net.SocketException; + method public static void bindSocket(java.io.FileDescriptor, java.net.SocketAddress) throws android.system.ErrnoException, java.net.SocketException; method public static void bindSocketToInterface(java.io.FileDescriptor, String) throws android.system.ErrnoException; method public static void closeSocket(java.io.FileDescriptor) throws java.io.IOException; + method public static void connectSocket(java.io.FileDescriptor, java.net.SocketAddress) throws android.system.ErrnoException, java.net.SocketException; method public static java.net.SocketAddress makeNetlinkSocketAddress(int, int); method public static java.net.SocketAddress makePacketSocketAddress(short, int); method public static java.net.SocketAddress makePacketSocketAddress(int, byte[]); + method public static void sendTo(java.io.FileDescriptor, byte[], int, int, int, java.net.SocketAddress) throws android.system.ErrnoException, java.net.SocketException; + method public static void setSocketTimeValueOption(java.io.FileDescriptor, int, int, long) throws android.system.ErrnoException; } } diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.cpp b/cmds/statsd/src/metrics/ValueMetricProducer.cpp index 4122d8440959..5645461cc3ab 100644 --- a/cmds/statsd/src/metrics/ValueMetricProducer.cpp +++ b/cmds/statsd/src/metrics/ValueMetricProducer.cpp @@ -524,6 +524,14 @@ void ValueMetricProducer::onMatchedLogEventInternalLocked(const size_t matcherIn multiIntervals.resize(mFieldMatchers.size()); } + // We only use anomaly detection under certain cases. + // N.B.: The anomaly detection cases were modified in order to fix an issue with value metrics + // containing multiple values. We tried to retain all previous behaviour, but we are unsure the + // previous behaviour was correct. At the time of the fix, anomaly detection had no owner. + // Whoever next works on it should look into the cases where it is triggered in this function. + // Discussion here: http://ag/6124370. + bool useAnomalyDetection = true; + for (int i = 0; i < (int)mFieldMatchers.size(); i++) { const Matcher& matcher = mFieldMatchers[i]; Interval& interval = multiIntervals[i]; @@ -546,7 +554,11 @@ void ValueMetricProducer::onMatchedLogEventInternalLocked(const size_t matcherIn // no base. just update base and return. interval.base = value; interval.hasBase = true; - return; + // If we're missing a base, do not use anomaly detection on incomplete data + useAnomalyDetection = false; + // Continue (instead of return) here in order to set interval.base and + // interval.hasBase for other intervals + continue; } } Value diff; @@ -560,7 +572,9 @@ void ValueMetricProducer::onMatchedLogEventInternalLocked(const size_t matcherIn VLOG("Unexpected decreasing value"); StatsdStats::getInstance().notePullDataError(mPullTagId); interval.base = value; - return; + // If we've got bad data, do not use anomaly detection + useAnomalyDetection = false; + continue; } break; case ValueMetric::DECREASING: @@ -572,7 +586,9 @@ void ValueMetricProducer::onMatchedLogEventInternalLocked(const size_t matcherIn VLOG("Unexpected increasing value"); StatsdStats::getInstance().notePullDataError(mPullTagId); interval.base = value; - return; + // If we've got bad data, do not use anomaly detection + useAnomalyDetection = false; + continue; } break; case ValueMetric::ANY: @@ -608,14 +624,18 @@ void ValueMetricProducer::onMatchedLogEventInternalLocked(const size_t matcherIn interval.sampleSize += 1; } - // TODO: propgate proper values down stream when anomaly support doubles - long wholeBucketVal = multiIntervals[0].value.long_value; - auto prev = mCurrentFullBucket.find(eventKey); - if (prev != mCurrentFullBucket.end()) { - wholeBucketVal += prev->second; - } - for (auto& tracker : mAnomalyTrackers) { - tracker->detectAndDeclareAnomaly(eventTimeNs, mCurrentBucketNum, eventKey, wholeBucketVal); + // Only trigger the tracker if all intervals are correct + if (useAnomalyDetection) { + // TODO: propgate proper values down stream when anomaly support doubles + long wholeBucketVal = multiIntervals[0].value.long_value; + auto prev = mCurrentFullBucket.find(eventKey); + if (prev != mCurrentFullBucket.end()) { + wholeBucketVal += prev->second; + } + for (auto& tracker : mAnomalyTrackers) { + tracker->detectAndDeclareAnomaly( + eventTimeNs, mCurrentBucketNum, eventKey, wholeBucketVal); + } } } diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.h b/cmds/statsd/src/metrics/ValueMetricProducer.h index 69eb0afaf7c4..a8dfc5ba0e5d 100644 --- a/cmds/statsd/src/metrics/ValueMetricProducer.h +++ b/cmds/statsd/src/metrics/ValueMetricProducer.h @@ -212,6 +212,7 @@ private: FRIEND_TEST(ValueMetricProducerTest, TestFirstBucket); FRIEND_TEST(ValueMetricProducerTest, TestCalcPreviousBucketEndTime); FRIEND_TEST(ValueMetricProducerTest, TestSkipZeroDiffOutput); + FRIEND_TEST(ValueMetricProducerTest, TestSkipZeroDiffOutputMultiValue); FRIEND_TEST(ValueMetricProducerTest, TestUseZeroDefaultBase); FRIEND_TEST(ValueMetricProducerTest, TestUseZeroDefaultBaseWithPullFailures); FRIEND_TEST(ValueMetricProducerTest, TestTrimUnusedDimensionKey); diff --git a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp index 9cfe3436ada7..c0648ee70032 100644 --- a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp +++ b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp @@ -1508,6 +1508,113 @@ TEST(ValueMetricProducerTest, TestSkipZeroDiffOutput) { EXPECT_EQ(5, valueProducer.mPastBuckets.begin()->second.back().values[0].long_value); } +TEST(ValueMetricProducerTest, TestSkipZeroDiffOutputMultiValue) { + ValueMetric metric; + metric.set_id(metricId); + metric.set_bucket(ONE_MINUTE); + metric.mutable_value_field()->set_field(tagId); + metric.mutable_value_field()->add_child()->set_field(2); + metric.mutable_value_field()->add_child()->set_field(3); + metric.set_aggregation_type(ValueMetric::MIN); + metric.set_use_diff(true); + + UidMap uidMap; + SimpleAtomMatcher atomMatcher; + atomMatcher.set_atom_id(tagId); + sp<EventMatcherWizard> eventMatcherWizard = + new EventMatcherWizard({new SimpleLogMatchingTracker( + atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)}); + sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); + sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>(); + + ValueMetricProducer valueProducer(kConfigKey, metric, -1, wizard, logEventMatcherIndex, + eventMatcherWizard, -1, bucketStartTimeNs, bucketStartTimeNs, + pullerManager); + + shared_ptr<LogEvent> event1 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10); + event1->write(1); + event1->write(10); + event1->write(20); + event1->init(); + shared_ptr<LogEvent> event2 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 15); + event2->write(1); + event2->write(15); + event2->write(22); + event2->init(); + valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event1); + // has one slice + EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size()); + ValueMetricProducer::Interval curInterval0 = + valueProducer.mCurrentSlicedBucket.begin()->second[0]; + ValueMetricProducer::Interval curInterval1 = + valueProducer.mCurrentSlicedBucket.begin()->second[1]; + EXPECT_EQ(true, curInterval0.hasBase); + EXPECT_EQ(10, curInterval0.base.long_value); + EXPECT_EQ(false, curInterval0.hasValue); + EXPECT_EQ(true, curInterval1.hasBase); + EXPECT_EQ(20, curInterval1.base.long_value); + EXPECT_EQ(false, curInterval1.hasValue); + + valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event2); + + // has one slice + EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size()); + curInterval0 = valueProducer.mCurrentSlicedBucket.begin()->second[0]; + curInterval1 = valueProducer.mCurrentSlicedBucket.begin()->second[1]; + EXPECT_EQ(true, curInterval0.hasValue); + EXPECT_EQ(5, curInterval0.value.long_value); + EXPECT_EQ(true, curInterval1.hasValue); + EXPECT_EQ(2, curInterval1.value.long_value); + + // no change in first value field + shared_ptr<LogEvent> event3 = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 10); + event3->write(1); + event3->write(15); + event3->write(25); + event3->init(); + valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event3); + EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size()); + curInterval0 = valueProducer.mCurrentSlicedBucket.begin()->second[0]; + curInterval1 = valueProducer.mCurrentSlicedBucket.begin()->second[1]; + EXPECT_EQ(true, curInterval0.hasBase); + EXPECT_EQ(15, curInterval0.base.long_value); + EXPECT_EQ(true, curInterval0.hasValue); + EXPECT_EQ(true, curInterval1.hasBase); + EXPECT_EQ(25, curInterval1.base.long_value); + EXPECT_EQ(true, curInterval1.hasValue); + + shared_ptr<LogEvent> event4 = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 15); + event4->write(1); + event4->write(15); + event4->write(29); + event4->init(); + valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event4); + EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size()); + curInterval0 = valueProducer.mCurrentSlicedBucket.begin()->second[0]; + curInterval1 = valueProducer.mCurrentSlicedBucket.begin()->second[1]; + EXPECT_EQ(true, curInterval0.hasBase); + EXPECT_EQ(15, curInterval0.base.long_value); + EXPECT_EQ(true, curInterval0.hasValue); + EXPECT_EQ(true, curInterval1.hasBase); + EXPECT_EQ(29, curInterval1.base.long_value); + EXPECT_EQ(true, curInterval1.hasValue); + + valueProducer.flushIfNeededLocked(bucket3StartTimeNs); + + EXPECT_EQ(1UL, valueProducer.mPastBuckets.size()); + EXPECT_EQ(2UL, valueProducer.mPastBuckets.begin()->second.size()); + EXPECT_EQ(2UL, valueProducer.mPastBuckets.begin()->second[0].values.size()); + EXPECT_EQ(1UL, valueProducer.mPastBuckets.begin()->second[1].values.size()); + + EXPECT_EQ(5, valueProducer.mPastBuckets.begin()->second[0].values[0].long_value); + EXPECT_EQ(0, valueProducer.mPastBuckets.begin()->second[0].valueIndex[0]); + EXPECT_EQ(2, valueProducer.mPastBuckets.begin()->second[0].values[1].long_value); + EXPECT_EQ(1, valueProducer.mPastBuckets.begin()->second[0].valueIndex[1]); + + EXPECT_EQ(3, valueProducer.mPastBuckets.begin()->second[1].values[0].long_value); + EXPECT_EQ(1, valueProducer.mPastBuckets.begin()->second[1].valueIndex[0]); +} + /* * Tests zero default base. */ diff --git a/core/java/android/net/IpPrefix.java b/core/java/android/net/IpPrefix.java index b996cdab5164..175263f0adaa 100644 --- a/core/java/android/net/IpPrefix.java +++ b/core/java/android/net/IpPrefix.java @@ -104,6 +104,8 @@ public final class IpPrefix implements Parcelable { * * @hide */ + @SystemApi + @TestApi public IpPrefix(String prefix) { // We don't reuse the (InetAddress, int) constructor because "error: call to this must be // first statement in constructor". We could factor out setting the member variables to an diff --git a/core/java/android/net/LinkAddress.java b/core/java/android/net/LinkAddress.java index fbd602c7b2d0..8d779aaa2312 100644 --- a/core/java/android/net/LinkAddress.java +++ b/core/java/android/net/LinkAddress.java @@ -176,6 +176,7 @@ public class LinkAddress implements Parcelable { * @hide */ @SystemApi + @TestApi public LinkAddress(InetAddress address, int prefixLength) { this(address, prefixLength, 0, 0); this.scope = scopeForUnicastAddress(address); @@ -199,6 +200,7 @@ public class LinkAddress implements Parcelable { * @hide */ @SystemApi + @TestApi public LinkAddress(String address) { this(address, 0, 0); this.scope = scopeForUnicastAddress(this.address); @@ -212,6 +214,8 @@ public class LinkAddress implements Parcelable { * @param scope The address scope. * @hide */ + @SystemApi + @TestApi public LinkAddress(String address, int flags, int scope) { // This may throw an IllegalArgumentException; catching it is the caller's responsibility. // TODO: consider rejecting mapped IPv4 addresses such as "::ffff:192.0.2.5/24". diff --git a/core/java/android/net/LinkProperties.java b/core/java/android/net/LinkProperties.java index 662870182eea..42db0fd7cb8c 100644 --- a/core/java/android/net/LinkProperties.java +++ b/core/java/android/net/LinkProperties.java @@ -287,7 +287,8 @@ public final class LinkProperties implements Parcelable { * @return true if {@code address} was added or updated, false otherwise. * @hide */ - @UnsupportedAppUsage + @SystemApi + @TestApi public boolean addLinkAddress(LinkAddress address) { if (address == null) { return false; @@ -315,6 +316,8 @@ public final class LinkProperties implements Parcelable { * @return true if the address was removed, false if it did not exist. * @hide */ + @SystemApi + @TestApi public boolean removeLinkAddress(LinkAddress toRemove) { int i = findLinkAddressIndex(toRemove); if (i >= 0) { diff --git a/core/java/android/net/Network.java b/core/java/android/net/Network.java index 2c831de72051..e04b5fc5f9cf 100644 --- a/core/java/android/net/Network.java +++ b/core/java/android/net/Network.java @@ -123,6 +123,8 @@ public class Network implements Parcelable { /** * @hide */ + @SystemApi + @TestApi public Network(Network that) { this(that.netId, that.mPrivateDnsBypass); } diff --git a/core/java/android/net/apf/ApfCapabilities.java b/core/java/android/net/apf/ApfCapabilities.java index baf5585589f1..e09fa8fd9e77 100644 --- a/core/java/android/net/apf/ApfCapabilities.java +++ b/core/java/android/net/apf/ApfCapabilities.java @@ -81,14 +81,14 @@ public class ApfCapabilities { /** * @return Whether the APF Filter in the device should filter out IEEE 802.3 Frames. */ - public boolean getApfDrop8023Frames(Context context) { + public static boolean getApfDrop8023Frames(Context context) { return context.getResources().getBoolean(R.bool.config_apfDrop802_3Frames); } /** * @return An array of blacklisted EtherType, packets with EtherTypes within it will be dropped. */ - public int[] getApfEthTypeBlackList(Context context) { + public static int[] getApfEthTypeBlackList(Context context) { return context.getResources().getIntArray(R.array.config_apfEthTypeBlackList); } } diff --git a/packages/NetworkStack/src/android/net/util/FdEventsReader.java b/core/java/android/net/shared/FdEventsReader.java index 8bbf449f6374..5ccc560a3091 100644 --- a/packages/NetworkStack/src/android/net/util/FdEventsReader.java +++ b/core/java/android/net/shared/FdEventsReader.java @@ -14,10 +14,10 @@ * limitations under the License. */ -package android.net.util; +package android.net.shared; -import static android.os.MessageQueue.OnFileDescriptorEventListener.EVENT_INPUT; import static android.os.MessageQueue.OnFileDescriptorEventListener.EVENT_ERROR; +import static android.os.MessageQueue.OnFileDescriptorEventListener.EVENT_INPUT; import android.annotation.NonNull; import android.annotation.Nullable; @@ -63,6 +63,7 @@ import java.io.FileDescriptor; * All public methods MUST only be called from the same thread with which * the Handler constructor argument is associated. * + * @param <BufferType> the type of the buffer used to read data. * @hide */ public abstract class FdEventsReader<BufferType> { @@ -89,6 +90,7 @@ public abstract class FdEventsReader<BufferType> { mBuffer = buffer; } + /** Start this FdEventsReader. */ public void start() { if (onCorrectThread()) { createAndRegisterFd(); @@ -100,6 +102,7 @@ public abstract class FdEventsReader<BufferType> { } } + /** Stop this FdEventsReader and destroy the file descriptor. */ public void stop() { if (onCorrectThread()) { unregisterAndDestroyFd(); @@ -112,18 +115,25 @@ public abstract class FdEventsReader<BufferType> { } @NonNull - public Handler getHandler() { return mHandler; } + public Handler getHandler() { + return mHandler; + } protected abstract int recvBufSize(@NonNull BufferType buffer); - public int recvBufSize() { return recvBufSize(mBuffer); } + /** Returns the size of the receive buffer. */ + public int recvBufSize() { + return recvBufSize(mBuffer); + } /** * Get the number of successful calls to {@link #readPacket(FileDescriptor, Object)}. * * <p>A call was successful if {@link #readPacket(FileDescriptor, Object)} returned a value > 0. */ - public final long numPacketsReceived() { return mPacketsReceived; } + public final long numPacketsReceived() { + return mPacketsReceived; + } /** * Subclasses MUST create the listening socket here, including setting @@ -199,7 +209,9 @@ public abstract class FdEventsReader<BufferType> { onStart(); } - private boolean isRunning() { return (mFd != null) && mFd.valid(); } + private boolean isRunning() { + return (mFd != null) && mFd.valid(); + } // Keep trying to read until we get EAGAIN/EWOULDBLOCK or some fatal error. private boolean handleInput() { diff --git a/core/java/android/net/util/SocketUtils.java b/core/java/android/net/util/SocketUtils.java index 2df08a1c5ba1..fbb15ed693d9 100644 --- a/core/java/android/net/util/SocketUtils.java +++ b/core/java/android/net/util/SocketUtils.java @@ -21,17 +21,21 @@ import static android.system.OsConstants.SO_BINDTODEVICE; 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. @@ -76,11 +80,80 @@ public class SocketUtils { } /** + * Set an option on a socket that takes a time value argument. + */ + public static void setSocketTimeValueOption( + FileDescriptor fd, int level, int option, long millis) throws ErrnoException { + Os.setsockoptTimeval(fd, level, option, StructTimeval.fromMillis(millis)); + } + + /** + * Bind a socket to the specified address. + */ + public static void bindSocket(FileDescriptor fd, SocketAddress addr) + throws ErrnoException, SocketException { + Os.bind(fd, addr); + } + + /** + * Connect a socket to the specified address. + */ + public static void connectSocket(FileDescriptor fd, SocketAddress addr) + throws ErrnoException, SocketException { + Os.connect(fd, addr); + } + + /** + * Send a message on a socket, using the specified SocketAddress. + */ + public static void sendTo(FileDescriptor fd, byte[] bytes, int byteOffset, int byteCount, + int flags, SocketAddress addr) throws ErrnoException, SocketException { + Os.sendto(fd, bytes, byteOffset, byteCount, flags, addr); + } + + /** * @see IoBridge#closeAndSignalBlockedThreads(FileDescriptor) */ public static void closeSocket(FileDescriptor fd) throws IOException { IoBridge.closeAndSignalBlockedThreads(fd); } + /** + * Attaches a socket filter that accepts DHCP packets to the given socket. + */ + public static void attachDhcpFilter(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(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(FileDescriptor fd, int packetType) + throws SocketException { + NetworkUtils.attachControlPacketFilter(fd, packetType); + } + + /** + * Add an entry into the ARP cache. + */ + public static void addArpEntry(Inet4Address ipv4Addr, MacAddress ethAddr, String ifname, + FileDescriptor fd) throws IOException { + NetworkUtils.addArpEntry(ipv4Addr, ethAddr, ifname, fd); + } + private SocketUtils() {} } diff --git a/core/java/android/os/GraphicsEnvironment.java b/core/java/android/os/GraphicsEnvironment.java index efcad3ece97d..93360a5fa377 100644 --- a/core/java/android/os/GraphicsEnvironment.java +++ b/core/java/android/os/GraphicsEnvironment.java @@ -63,7 +63,7 @@ public class GraphicsEnvironment { private static final boolean DEBUG = false; private static final String TAG = "GraphicsEnvironment"; private static final String PROPERTY_GFX_DRIVER = "ro.gfx.driver.0"; - private static final String GUP_WHITELIST_FILENAME = "whitelist.txt"; + private static final String GAME_DRIVER_WHITELIST_FILENAME = "whitelist.txt"; private static final String ANGLE_RULES_FILE = "a4a_rules.json"; private static final String ANGLE_TEMP_RULES = "debug.angle.rules"; private static final String ACTION_ANGLE_FOR_ANDROID = "android.app.action.ANGLE_FOR_ANDROID"; @@ -529,44 +529,45 @@ public class GraphicsEnvironment { return; } - // GUP_DEV_ALL_APPS + // GAME_DRIVER_ALL_APPS // 0: Default (Invalid values fallback to default as well) // 1: All apps use Game Driver // 2: All apps use system graphics driver - int gupDevAllApps = coreSettings.getInt(Settings.Global.GUP_DEV_ALL_APPS, 0); - if (gupDevAllApps == 2) { + int gameDriverAllApps = coreSettings.getInt(Settings.Global.GAME_DRIVER_ALL_APPS, 0); + if (gameDriverAllApps == 2) { if (DEBUG) { - Log.w(TAG, "GUP is turned off on this device"); + Log.w(TAG, "Game Driver is turned off on this device"); } return; } - if (gupDevAllApps != 1) { - // GUP_DEV_OPT_OUT_APPS has higher priority than GUP_DEV_OPT_IN_APPS - if (getGlobalSettingsString(coreSettings, Settings.Global.GUP_DEV_OPT_OUT_APPS) + if (gameDriverAllApps != 1) { + // GAME_DRIVER_OPT_OUT_APPS has higher priority than GAME_DRIVER_OPT_IN_APPS + if (getGlobalSettingsString(coreSettings, Settings.Global.GAME_DRIVER_OPT_OUT_APPS) .contains(ai.packageName)) { if (DEBUG) { - Log.w(TAG, ai.packageName + " opts out from GUP."); + Log.w(TAG, ai.packageName + " opts out from Game Driver."); } return; } - boolean isDevOptIn = getGlobalSettingsString(coreSettings, - Settings.Global.GUP_DEV_OPT_IN_APPS) - .contains(ai.packageName); + boolean isOptIn = + getGlobalSettingsString(coreSettings, Settings.Global.GAME_DRIVER_OPT_IN_APPS) + .contains(ai.packageName); - if (!isDevOptIn && !onWhitelist(context, driverPackageName, ai.packageName)) { + if (!isOptIn && !onWhitelist(context, driverPackageName, ai.packageName)) { if (DEBUG) { Log.w(TAG, ai.packageName + " is not on the whitelist."); } return; } - if (!isDevOptIn) { + if (!isOptIn) { // At this point, the application is on the whitelist only, check whether it's // on the blacklist, terminate early when it's on the blacklist. try { // TODO(b/121350991) Switch to DeviceConfig with property listener. - String base64String = coreSettings.getString(Settings.Global.GUP_BLACKLIST); + String base64String = + coreSettings.getString(Settings.Global.GAME_DRIVER_BLACKLIST); if (base64String != null && !base64String.isEmpty()) { Blacklists blacklistsProto = Blacklists.parseFrom( Base64.decode(base64String, BASE64_FLAGS)); @@ -652,7 +653,7 @@ public class GraphicsEnvironment { Context driverContext = context.createPackageContext(driverPackageName, Context.CONTEXT_RESTRICTED); AssetManager assets = driverContext.getAssets(); - InputStream stream = assets.open(GUP_WHITELIST_FILENAME); + InputStream stream = assets.open(GAME_DRIVER_WHITELIST_FILENAME); BufferedReader reader = new BufferedReader(new InputStreamReader(stream)); for (String packageName; (packageName = reader.readLine()) != null; ) { if (packageName.equals(applicationPackageName)) { diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 290b5560474b..a7af5d191c17 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -12241,33 +12241,33 @@ public final class Settings { "angle_whitelist"; /** - * Game Update Package global preference for all Apps. + * Game Driver global preference for all Apps. * 0 = Default - * 1 = All Apps use Game Update Package + * 1 = All Apps use Game Driver * 2 = All Apps use system graphics driver * @hide */ - public static final String GUP_DEV_ALL_APPS = "gup_dev_all_apps"; + public static final String GAME_DRIVER_ALL_APPS = "game_driver_all_apps"; /** - * List of Apps selected to use Game Update Package. + * List of Apps selected to use Game Driver. * i.e. <pkg1>,<pkg2>,...,<pkgN> * @hide */ - public static final String GUP_DEV_OPT_IN_APPS = "gup_dev_opt_in_apps"; + public static final String GAME_DRIVER_OPT_IN_APPS = "game_driver_opt_in_apps"; /** - * List of Apps selected not to use Game Update Package. + * List of Apps selected not to use Game Driver. * i.e. <pkg1>,<pkg2>,...,<pkgN> * @hide */ - public static final String GUP_DEV_OPT_OUT_APPS = "gup_dev_opt_out_apps"; + public static final String GAME_DRIVER_OPT_OUT_APPS = "game_driver_opt_out_apps"; /** - * Apps on the blacklist that are forbidden to use Game Update Package. + * Apps on the blacklist that are forbidden to use Game Driver. * @hide */ - public static final String GUP_BLACKLIST = "gup_blacklist"; + public static final String GAME_DRIVER_BLACKLIST = "game_driver_blacklist"; /** * Apps on the whitelist that are allowed to use Game Driver. diff --git a/core/java/android/view/IDisplayFoldListener.aidl b/core/java/android/view/IDisplayFoldListener.aidl new file mode 100644 index 000000000000..2c91149dfc1b --- /dev/null +++ b/core/java/android/view/IDisplayFoldListener.aidl @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.view; + +/** + * {@hide} + */ +oneway interface IDisplayFoldListener +{ + /** Called when the foldedness of a display changes */ + void onDisplayFoldChanged(int displayId, boolean folded); +} diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl index 42ac8801629f..8ae4757f5de6 100644 --- a/core/java/android/view/IWindowManager.aidl +++ b/core/java/android/view/IWindowManager.aidl @@ -34,6 +34,7 @@ import android.os.ParcelFileDescriptor; import android.view.IApplicationToken; import android.view.IAppTransitionAnimationSpecsFuture; import android.view.IDockedStackListener; +import android.view.IDisplayFoldListener; import android.view.IOnKeyguardExitResult; import android.view.IPinnedStackListener; import android.view.RemoteAnimationAdapter; @@ -403,6 +404,16 @@ interface IWindowManager Region getCurrentImeTouchRegion(); /** + * Registers an IDisplayFoldListener. + */ + void registerDisplayFoldListener(IDisplayFoldListener listener); + + /** + * Unregisters an IDisplayFoldListener. + */ + void unregisterDisplayFoldListener(IDisplayFoldListener listener); + + /** * Starts a window trace. */ void startWindowTrace(); diff --git a/core/proto/android/app/settings_enums.proto b/core/proto/android/app/settings_enums.proto index eb716ac280e2..94a6734b69c8 100644 --- a/core/proto/android/app/settings_enums.proto +++ b/core/proto/android/app/settings_enums.proto @@ -2157,10 +2157,10 @@ enum PageId { // OS: Q ZEN_CUSTOM_SETTINGS_DIALOG = 1612; - // OPEN: Settings > Developer Options > Game Update Packages + // OPEN: Settings > Developer Options > Game Driver Preferences // CATEGORY: SETTINGS // OS: Q - SETTINGS_GUP_DASHBOARD = 1613; + SETTINGS_GAME_DRIVER_DASHBOARD = 1613; // OPEN: Settings > Accessibility > Vibration > Ring vibration // CATEGORY: SETTINGS diff --git a/core/proto/android/providers/settings/global.proto b/core/proto/android/providers/settings/global.proto index 7e7942e6ddf1..66cd1094825a 100644 --- a/core/proto/android/providers/settings/global.proto +++ b/core/proto/android/providers/settings/global.proto @@ -436,20 +436,20 @@ message GlobalSettingsProto { // Ordered GPU debug layer list for GLES // i.e. <layer1>:<layer2>:...:<layerN> optional SettingProto debug_layers_gles = 7; - // GUP - Game Update Package global preference for all Apps + // Game Driver - global preference for all Apps // 0 = Default - // 1 = All Apps use Game Update Package + // 1 = All Apps use Game Driver // 2 = All Apps use system graphics driver - optional SettingProto gup_dev_all_apps = 8; - // GUP - List of Apps selected to use Game Update Package + optional SettingProto game_driver_all_apps = 8; + // Game Driver - List of Apps selected to use Game Driver // i.e. <pkg1>,<pkg2>,...,<pkgN> - optional SettingProto gup_dev_opt_in_apps = 9; - // GUP - List of Apps selected not to use Game Update Package + optional SettingProto game_driver_opt_in_apps = 9; + // Game Driver - List of Apps selected not to use Game Driver // i.e. <pkg1>,<pkg2>,...,<pkgN> - optional SettingProto gup_dev_opt_out_apps = 10; - // GUP - List of Apps that are forbidden to use Game Update Package - optional SettingProto gup_blacklist = 11; - // List of Apps that are allowed to use Game Driver package. + optional SettingProto game_driver_opt_out_apps = 10; + // Game Driver - List of Apps that are forbidden to use Game Driver + optional SettingProto game_driver_blacklist = 11; + // Game Driver - List of Apps that are allowed to use Game Driver optional SettingProto game_driver_whitelist = 12; // ANGLE - List of Apps that can check ANGLE rules optional SettingProto angle_whitelist = 13; diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index c05795de4751..1db8135d31c7 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -861,6 +861,11 @@ The default is false. --> <bool name="config_lidControlsSleep">false</bool> + <!-- Indicate whether closing the lid causes the device to enter the folded state which means + to get a smaller screen and opening the lid causes the device to enter the unfolded state + which means to get a larger screen. --> + <bool name="config_lidControlsDisplayFold">false</bool> + <!-- Desk dock behavior --> <!-- The number of degrees to rotate the display when the device is in a desk dock. diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 47bd9da65870..aefa9dfd1056 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -3532,6 +3532,7 @@ <java-symbol type="integer" name="config_defaultRingVibrationIntensity" /> <java-symbol type="bool" name="config_maskMainBuiltInDisplayCutout" /> + <java-symbol type="bool" name="config_lidControlsDisplayFold" /> <java-symbol type="array" name="config_disableApksUnlessMatchedSku_apk_list" /> <java-symbol type="array" name="config_disableApkUnlessMatchedSku_skus_list" /> diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java index 605c219072bc..ca2e3ed0b5ab 100644 --- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java +++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java @@ -486,10 +486,10 @@ public class SettingsBackupTest { Settings.Global.GLOBAL_SETTINGS_ANGLE_GL_DRIVER_SELECTION_PKGS, Settings.Global.GLOBAL_SETTINGS_ANGLE_GL_DRIVER_SELECTION_VALUES, Settings.Global.GLOBAL_SETTINGS_ANGLE_WHITELIST, - Settings.Global.GUP_DEV_ALL_APPS, - Settings.Global.GUP_DEV_OPT_IN_APPS, - Settings.Global.GUP_DEV_OPT_OUT_APPS, - Settings.Global.GUP_BLACKLIST, + Settings.Global.GAME_DRIVER_ALL_APPS, + Settings.Global.GAME_DRIVER_OPT_IN_APPS, + Settings.Global.GAME_DRIVER_OPT_OUT_APPS, + Settings.Global.GAME_DRIVER_BLACKLIST, Settings.Global.GAME_DRIVER_WHITELIST, Settings.Global.GPU_DEBUG_LAYER_APP, Settings.Global.ENABLE_GNSS_RAW_MEAS_FULL_TRACKING, diff --git a/packages/NetworkStack/src/android/net/dhcp/DhcpPacketListener.java b/packages/NetworkStack/src/android/net/dhcp/DhcpPacketListener.java index dce8b619494e..eac8d2a3b410 100644 --- a/packages/NetworkStack/src/android/net/dhcp/DhcpPacketListener.java +++ b/packages/NetworkStack/src/android/net/dhcp/DhcpPacketListener.java @@ -18,7 +18,7 @@ package android.net.dhcp; import android.annotation.NonNull; import android.annotation.Nullable; -import android.net.util.FdEventsReader; +import android.net.shared.FdEventsReader; import android.os.Handler; import android.system.Os; diff --git a/packages/NetworkStack/src/android/net/ip/IpClient.java b/packages/NetworkStack/src/android/net/ip/IpClient.java index f20e01636d72..4315d34ba447 100644 --- a/packages/NetworkStack/src/android/net/ip/IpClient.java +++ b/packages/NetworkStack/src/android/net/ip/IpClient.java @@ -40,15 +40,12 @@ import android.net.ip.IIpClientCallbacks; import android.net.metrics.IpConnectivityLog; import android.net.metrics.IpManagerEvent; import android.net.shared.InitialConfiguration; -import android.net.shared.NetdService; import android.net.shared.ProvisioningConfiguration; import android.net.util.InterfaceParams; import android.net.util.SharedLog; import android.os.ConditionVariable; -import android.os.INetworkManagementService; import android.os.Message; import android.os.RemoteException; -import android.os.ServiceManager; import android.os.SystemClock; import android.text.TextUtils; import android.util.LocalLog; @@ -64,7 +61,7 @@ import com.android.internal.util.Preconditions; import com.android.internal.util.State; import com.android.internal.util.StateMachine; import com.android.internal.util.WakeupMessage; -import com.android.server.net.NetlinkTracker; +import com.android.server.NetworkObserverRegistry; import java.io.FileDescriptor; import java.io.PrintWriter; @@ -338,8 +335,9 @@ public class IpClient extends StateMachine { private final Dependencies mDependencies; private final CountDownLatch mShutdownLatch; private final ConnectivityManager mCm; - private final INetworkManagementService mNwService; - private final NetlinkTracker mNetlinkTracker; + private final INetd mNetd; + private final NetworkObserverRegistry mObserverRegistry; + private final IpClientLinkObserver mLinkObserver; private final WakeupMessage mProvisioningTimeoutAlarm; private final WakeupMessage mDhcpActionTimeoutAlarm; private final SharedLog mLog; @@ -373,15 +371,6 @@ public class IpClient extends StateMachine { private final ConditionVariable mApfDataSnapshotComplete = new ConditionVariable(); public static class Dependencies { - public INetworkManagementService getNMS() { - return INetworkManagementService.Stub.asInterface( - ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE)); - } - - public INetd getNetd() { - return NetdService.getInstance(); - } - /** * Get interface parameters for the specified interface. */ @@ -390,26 +379,14 @@ public class IpClient extends StateMachine { } } - public IpClient(Context context, String ifName, IIpClientCallbacks callback) { - this(context, ifName, callback, new Dependencies()); - } - - /** - * An expanded constructor, useful for dependency injection. - * TODO: migrate all test users to mock IpClient directly and remove this ctor. - */ public IpClient(Context context, String ifName, IIpClientCallbacks callback, - INetworkManagementService nwService) { - this(context, ifName, callback, new Dependencies() { - @Override - public INetworkManagementService getNMS() { - return nwService; - } - }); + NetworkObserverRegistry observerRegistry) { + this(context, ifName, callback, observerRegistry, new Dependencies()); } @VisibleForTesting - IpClient(Context context, String ifName, IIpClientCallbacks callback, Dependencies deps) { + IpClient(Context context, String ifName, IIpClientCallbacks callback, + NetworkObserverRegistry observerRegistry, Dependencies deps) { super(IpClient.class.getSimpleName() + "." + ifName); Preconditions.checkNotNull(ifName); Preconditions.checkNotNull(callback); @@ -422,7 +399,7 @@ public class IpClient extends StateMachine { mDependencies = deps; mShutdownLatch = new CountDownLatch(1); mCm = mContext.getSystemService(ConnectivityManager.class); - mNwService = deps.getNMS(); + mObserverRegistry = observerRegistry; sSmLogs.putIfAbsent(mInterfaceName, new SharedLog(MAX_LOG_RECORDS, mTag)); mLog = sSmLogs.get(mInterfaceName); @@ -433,19 +410,15 @@ public class IpClient extends StateMachine { // TODO: Consider creating, constructing, and passing in some kind of // InterfaceController.Dependencies class. - mInterfaceCtrl = new InterfaceController(mInterfaceName, deps.getNetd(), mLog); + mNetd = mContext.getSystemService(INetd.class); + mInterfaceCtrl = new InterfaceController(mInterfaceName, mNetd, mLog); - mNetlinkTracker = new NetlinkTracker( + mLinkObserver = new IpClientLinkObserver( mInterfaceName, - new NetlinkTracker.Callback() { - @Override - public void update() { - sendMessage(EVENT_NETLINK_LINKPROPERTIES_CHANGED); - } - }) { + () -> sendMessage(EVENT_NETLINK_LINKPROPERTIES_CHANGED)) { @Override - public void interfaceAdded(String iface) { - super.interfaceAdded(iface); + public void onInterfaceAdded(String iface) { + super.onInterfaceAdded(iface); if (mClatInterfaceName.equals(iface)) { mCallback.setNeighborDiscoveryOffload(false); } else if (!mInterfaceName.equals(iface)) { @@ -457,8 +430,8 @@ public class IpClient extends StateMachine { } @Override - public void interfaceRemoved(String iface) { - super.interfaceRemoved(iface); + public void onInterfaceRemoved(String iface) { + super.onInterfaceRemoved(iface); // TODO: Also observe mInterfaceName going down and take some // kind of appropriate action. if (mClatInterfaceName.equals(iface)) { @@ -570,19 +543,11 @@ public class IpClient extends StateMachine { } private void startStateMachineUpdaters() { - try { - mNwService.registerObserver(mNetlinkTracker); - } catch (RemoteException e) { - logError("Couldn't register NetlinkTracker: %s", e); - } + mObserverRegistry.registerObserverForNonblockingCallback(mLinkObserver); } private void stopStateMachineUpdaters() { - try { - mNwService.unregisterObserver(mNetlinkTracker); - } catch (RemoteException e) { - logError("Couldn't unregister NetlinkTracker: %s", e); - } + mObserverRegistry.unregisterObserver(mLinkObserver); } @Override @@ -805,7 +770,7 @@ public class IpClient extends StateMachine { // we should only call this if we know for sure that there are no IP addresses // assigned to the interface, etc. private void resetLinkProperties() { - mNetlinkTracker.clearLinkProperties(); + mLinkObserver.clearLinkProperties(); mConfiguration = null; mDhcpResults = null; mTcpBufferSizes = ""; @@ -984,10 +949,10 @@ public class IpClient extends StateMachine { // - IPv6 DNS servers // // N.B.: this is fundamentally race-prone and should be fixed by - // changing NetlinkTracker from a hybrid edge/level model to an + // changing IpClientLinkObserver from a hybrid edge/level model to an // edge-only model, or by giving IpClient its own netlink socket(s) // so as to track all required information directly. - LinkProperties netlinkLinkProperties = mNetlinkTracker.getLinkProperties(); + LinkProperties netlinkLinkProperties = mLinkObserver.getLinkProperties(); newLp.setLinkAddresses(netlinkLinkProperties.getLinkAddresses()); for (RouteInfo route : netlinkLinkProperties.getRoutes()) { newLp.addRoute(route); @@ -1166,8 +1131,7 @@ public class IpClient extends StateMachine { // necessary or does reading from settings at startup suffice?). final int numSolicits = 5; final int interSolicitIntervalMs = 750; - setNeighborParameters(mDependencies.getNetd(), mInterfaceName, - numSolicits, interSolicitIntervalMs); + setNeighborParameters(mNetd, mInterfaceName, numSolicits, interSolicitIntervalMs); } catch (Exception e) { mLog.e("Failed to adjust neighbor parameters", e); // Carry on using the system defaults (currently: 3, 1000); diff --git a/packages/NetworkStack/src/android/net/ip/IpClientLinkObserver.java b/packages/NetworkStack/src/android/net/ip/IpClientLinkObserver.java new file mode 100644 index 000000000000..8ad99aa0399a --- /dev/null +++ b/packages/NetworkStack/src/android/net/ip/IpClientLinkObserver.java @@ -0,0 +1,378 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.net.ip; + +import android.net.InetAddresses; +import android.net.LinkAddress; +import android.net.LinkProperties; +import android.net.RouteInfo; +import android.util.Log; + +import com.android.server.NetworkObserver; + +import java.net.InetAddress; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Set; + +/** + * Keeps track of link configuration received from Netd. + * + * An instance of this class is constructed by passing in an interface name and a callback. The + * owner is then responsible for registering the tracker with NetworkObserverRegistry. When the + * class receives update notifications, it applies the update to its local LinkProperties, and if + * something has changed, notifies its owner of the update via the callback. + * + * The owner can then call {@code getLinkProperties()} in order to find out + * what changed. If in the meantime the LinkProperties stored here have changed, + * this class will return the current LinkProperties. Because each change + * triggers an update callback after the change is made, the owner may get more + * callbacks than strictly necessary (some of which may be no-ops), but will not + * be out of sync once all callbacks have been processed. + * + * Threading model: + * + * - The owner of this class is expected to create it, register it, and call + * getLinkProperties or clearLinkProperties on its thread. + * - Most of the methods in the class are implementing NetworkObserver and are called + * on the handler used to register the observer. + * - All accesses to mLinkProperties must be synchronized(this). All the other + * member variables are immutable once the object is constructed. + * + * @hide + */ +public class IpClientLinkObserver implements NetworkObserver { + private final String mTag; + + /** + * Callback used by {@link IpClientLinkObserver} to send update notifications. + */ + public interface Callback { + /** + * Called when some properties of the link were updated. + */ + void update(); + } + + private final String mInterfaceName; + private final Callback mCallback; + private final LinkProperties mLinkProperties; + private DnsServerRepository mDnsServerRepository; + + private static final boolean DBG = false; + + public IpClientLinkObserver(String iface, Callback callback) { + mTag = "NetlinkTracker/" + iface; + mInterfaceName = iface; + mCallback = callback; + mLinkProperties = new LinkProperties(); + mLinkProperties.setInterfaceName(mInterfaceName); + mDnsServerRepository = new DnsServerRepository(); + } + + private void maybeLog(String operation, String iface, LinkAddress address) { + if (DBG) { + Log.d(mTag, operation + ": " + address + " on " + iface + + " flags " + address.getFlags() + " scope " + address.getScope()); + } + } + + private void maybeLog(String operation, Object o) { + if (DBG) { + Log.d(mTag, operation + ": " + o.toString()); + } + } + + @Override + public void onInterfaceRemoved(String iface) { + maybeLog("interfaceRemoved", iface); + if (mInterfaceName.equals(iface)) { + // Our interface was removed. Clear our LinkProperties and tell our owner that they are + // now empty. Note that from the moment that the interface is removed, any further + // interface-specific messages (e.g., RTM_DELADDR) will not reach us, because the netd + // code that parses them will not be able to resolve the ifindex to an interface name. + clearLinkProperties(); + mCallback.update(); + } + } + + @Override + public void onInterfaceAddressUpdated(LinkAddress address, String iface) { + if (mInterfaceName.equals(iface)) { + maybeLog("addressUpdated", iface, address); + boolean changed; + synchronized (this) { + changed = mLinkProperties.addLinkAddress(address); + } + if (changed) { + mCallback.update(); + } + } + } + + @Override + public void onInterfaceAddressRemoved(LinkAddress address, String iface) { + if (mInterfaceName.equals(iface)) { + maybeLog("addressRemoved", iface, address); + boolean changed; + synchronized (this) { + changed = mLinkProperties.removeLinkAddress(address); + } + if (changed) { + mCallback.update(); + } + } + } + + @Override + public void onRouteUpdated(RouteInfo route) { + if (mInterfaceName.equals(route.getInterface())) { + maybeLog("routeUpdated", route); + boolean changed; + synchronized (this) { + changed = mLinkProperties.addRoute(route); + } + if (changed) { + mCallback.update(); + } + } + } + + @Override + public void onRouteRemoved(RouteInfo route) { + if (mInterfaceName.equals(route.getInterface())) { + maybeLog("routeRemoved", route); + boolean changed; + synchronized (this) { + changed = mLinkProperties.removeRoute(route); + } + if (changed) { + mCallback.update(); + } + } + } + + @Override + public void onInterfaceDnsServerInfo(String iface, long lifetime, String[] addresses) { + if (mInterfaceName.equals(iface)) { + maybeLog("interfaceDnsServerInfo", Arrays.toString(addresses)); + boolean changed = mDnsServerRepository.addServers(lifetime, addresses); + if (changed) { + synchronized (this) { + mDnsServerRepository.setDnsServersOn(mLinkProperties); + } + mCallback.update(); + } + } + } + + /** + * Returns a copy of this object's LinkProperties. + */ + public synchronized LinkProperties getLinkProperties() { + return new LinkProperties(mLinkProperties); + } + + /** + * Reset this object's LinkProperties. + */ + public synchronized void clearLinkProperties() { + // Clear the repository before clearing mLinkProperties. That way, if a clear() happens + // while interfaceDnsServerInfo() is being called, we'll end up with no DNS servers in + // mLinkProperties, as desired. + mDnsServerRepository = new DnsServerRepository(); + mLinkProperties.clear(); + mLinkProperties.setInterfaceName(mInterfaceName); + } + + /** + * Tracks DNS server updates received from Netlink. + * + * The network may announce an arbitrary number of DNS servers in Router Advertisements at any + * time. Each announcement has a lifetime; when the lifetime expires, the servers should not be + * used any more. In this way, the network can gracefully migrate clients from one set of DNS + * servers to another. Announcements can both raise and lower the lifetime, and an announcement + * can expire servers by announcing them with a lifetime of zero. + * + * Typically the system will only use a small number (2 or 3; {@code NUM_CURRENT_SERVERS}) of + * DNS servers at any given time. These are referred to as the current servers. In case all the + * current servers expire, the class also keeps track of a larger (but limited) number of + * servers that are promoted to current servers when the current ones expire. In order to + * minimize updates to the rest of the system (and potentially expensive cache flushes) this + * class attempts to keep the list of current servers constant where possible. More + * specifically, the list of current servers is only updated if a new server is learned and + * there are not yet {@code NUM_CURRENT_SERVERS} current servers, or if one or more of the + * current servers expires or is pushed out of the set. Therefore, the current servers will not + * necessarily be the ones with the highest lifetime, but the ones learned first. + * + * This is by design: if instead the class always preferred the servers with the highest + * lifetime, a (misconfigured?) network where two or more routers announce more than + * {@code NUM_CURRENT_SERVERS} unique servers would cause persistent oscillations. + * + * TODO: Currently servers are only expired when a new DNS update is received. + * Update them using timers, or possibly on every notification received by NetlinkTracker. + * + * Threading model: run by NetlinkTracker. Methods are synchronized(this) just in case netlink + * notifications are sent by multiple threads. If future threads use alarms to expire, those + * alarms must also be synchronized(this). + * + */ + private static class DnsServerRepository { + + /** How many DNS servers we will use. 3 is suggested by RFC 6106. */ + static final int NUM_CURRENT_SERVERS = 3; + + /** How many DNS servers we'll keep track of, in total. */ + static final int NUM_SERVERS = 12; + + /** Stores up to {@code NUM_CURRENT_SERVERS} DNS servers we're currently using. */ + private Set<InetAddress> mCurrentServers; + + public static final String TAG = "DnsServerRepository"; + + /** + * Stores all the DNS servers we know about, for use when the current servers expire. + * Always sorted in order of decreasing expiry. The elements in this list are also the + * values of mIndex, and may be elements in mCurrentServers. + */ + private ArrayList<DnsServerEntry> mAllServers; + + /** + * Indexes the servers so we can update their lifetimes more quickly in the common case + * where servers are not being added, but only being refreshed. + */ + private HashMap<InetAddress, DnsServerEntry> mIndex; + + DnsServerRepository() { + mCurrentServers = new HashSet<>(); + mAllServers = new ArrayList<>(NUM_SERVERS); + mIndex = new HashMap<>(NUM_SERVERS); + } + + /** Sets the DNS servers of the provided LinkProperties object to the current servers. */ + public synchronized void setDnsServersOn(LinkProperties lp) { + lp.setDnsServers(mCurrentServers); + } + + /** + * Notifies the class of new DNS server information. + * @param lifetime the time in seconds that the DNS servers are valid. + * @param addresses the string representations of the IP addresses of DNS servers to use. + */ + public synchronized boolean addServers(long lifetime, String[] addresses) { + // The lifetime is actually an unsigned 32-bit number, but Java doesn't have unsigned. + // Technically 0xffffffff (the maximum) is special and means "forever", but 2^32 seconds + // (136 years) is close enough. + long now = System.currentTimeMillis(); + long expiry = now + 1000 * lifetime; + + // Go through the list of servers. For each one, update the entry if one exists, and + // create one if it doesn't. + for (String addressString : addresses) { + InetAddress address; + try { + address = InetAddresses.parseNumericAddress(addressString); + } catch (IllegalArgumentException ex) { + continue; + } + + if (!updateExistingEntry(address, expiry)) { + // There was no entry for this server. Create one, unless it's already expired + // (i.e., if the lifetime is zero; it cannot be < 0 because it's unsigned). + if (expiry > now) { + DnsServerEntry entry = new DnsServerEntry(address, expiry); + mAllServers.add(entry); + mIndex.put(address, entry); + } + } + } + + // Sort the servers by expiry. + Collections.sort(mAllServers); + + // Prune excess entries and update the current server list. + return updateCurrentServers(); + } + + private synchronized boolean updateExistingEntry(InetAddress address, long expiry) { + DnsServerEntry existing = mIndex.get(address); + if (existing != null) { + existing.expiry = expiry; + return true; + } + return false; + } + + private synchronized boolean updateCurrentServers() { + long now = System.currentTimeMillis(); + boolean changed = false; + + // Prune excess or expired entries. + for (int i = mAllServers.size() - 1; i >= 0; i--) { + if (i >= NUM_SERVERS || mAllServers.get(i).expiry < now) { + DnsServerEntry removed = mAllServers.remove(i); + mIndex.remove(removed.address); + changed |= mCurrentServers.remove(removed.address); + } else { + break; + } + } + + // Add servers to the current set, in order of decreasing lifetime, until it has enough. + // Prefer existing servers over new servers in order to minimize updates to the rest of + // the system and avoid persistent oscillations. + for (DnsServerEntry entry : mAllServers) { + if (mCurrentServers.size() < NUM_CURRENT_SERVERS) { + changed |= mCurrentServers.add(entry.address); + } else { + break; + } + } + return changed; + } + } + + /** + * Represents a DNS server entry with an expiry time. + * + * Implements Comparable so DNS server entries can be sorted by lifetime, longest-lived first. + * The ordering of entries with the same lifetime is unspecified, because given two servers with + * identical lifetimes, we don't care which one we use, and only comparing the lifetime is much + * faster than comparing the IP address as well. + * + * Note: this class has a natural ordering that is inconsistent with equals. + */ + private static class DnsServerEntry implements Comparable<DnsServerEntry> { + /** The IP address of the DNS server. */ + public final InetAddress address; + /** The time until which the DNS server may be used. A Java millisecond time as might be + * returned by currentTimeMillis(). */ + public long expiry; + + DnsServerEntry(InetAddress address, long expiry) throws IllegalArgumentException { + this.address = address; + this.expiry = expiry; + } + + public int compareTo(DnsServerEntry other) { + return Long.compare(other.expiry, this.expiry); + } + } +} diff --git a/packages/NetworkStack/src/android/net/util/PacketReader.java b/packages/NetworkStack/src/android/net/util/PacketReader.java index 4aec6b6753a6..94b1e9f2e14e 100644 --- a/packages/NetworkStack/src/android/net/util/PacketReader.java +++ b/packages/NetworkStack/src/android/net/util/PacketReader.java @@ -18,6 +18,7 @@ package android.net.util; import static java.lang.Math.max; +import android.net.shared.FdEventsReader; import android.os.Handler; import android.system.Os; diff --git a/packages/NetworkStack/src/com/android/server/NetworkObserver.java b/packages/NetworkStack/src/com/android/server/NetworkObserver.java index d3b40a67633d..cccec0bb5d40 100644 --- a/packages/NetworkStack/src/com/android/server/NetworkObserver.java +++ b/packages/NetworkStack/src/com/android/server/NetworkObserver.java @@ -17,6 +17,7 @@ package com.android.server; import android.net.LinkAddress; +import android.net.RouteInfo; /** * Observer for network events, to use with {@link NetworkObserverRegistry}. @@ -77,11 +78,11 @@ public interface NetworkObserver { * @see android.net.INetdUnsolicitedEventListener * #onRouteChanged(boolean, String, String, String) */ - default void onRouteUpdated(String route, String gateway, String ifName) {} + default void onRouteUpdated(RouteInfo route) {} /** * @see android.net.INetdUnsolicitedEventListener * #onRouteChanged(boolean, String, String, String) */ - default void onRouteRemoved(String route, String gateway, String ifName) {} + default void onRouteRemoved(RouteInfo route) {} } diff --git a/packages/NetworkStack/src/com/android/server/NetworkObserverRegistry.java b/packages/NetworkStack/src/com/android/server/NetworkObserverRegistry.java index 14e6c5fdadb9..4f55779f473b 100644 --- a/packages/NetworkStack/src/com/android/server/NetworkObserverRegistry.java +++ b/packages/NetworkStack/src/com/android/server/NetworkObserverRegistry.java @@ -18,11 +18,16 @@ package com.android.server; import android.annotation.NonNull; import android.net.INetd; import android.net.INetdUnsolicitedEventListener; +import android.net.InetAddresses; +import android.net.IpPrefix; import android.net.LinkAddress; +import android.net.RouteInfo; import android.os.Handler; import android.os.RemoteException; +import android.util.Log; import java.util.Map; +import java.util.Optional; import java.util.concurrent.ConcurrentHashMap; /** @@ -32,6 +37,7 @@ import java.util.concurrent.ConcurrentHashMap; * all INetworkManagementEventObserver objects that have registered with it. */ public class NetworkObserverRegistry extends INetdUnsolicitedEventListener.Stub { + private static final String TAG = NetworkObserverRegistry.class.getSimpleName(); /** * Constructs a new NetworkObserverRegistry. @@ -50,7 +56,7 @@ public class NetworkObserverRegistry extends INetdUnsolicitedEventListener.Stub netd.registerUnsolicitedEventListener(this); } - private final ConcurrentHashMap<NetworkObserver, Handler> mObservers = + private final ConcurrentHashMap<NetworkObserver, Optional<Handler>> mObservers = new ConcurrentHashMap<>(); /** @@ -58,7 +64,20 @@ public class NetworkObserverRegistry extends INetdUnsolicitedEventListener.Stub * This method may be called on any thread. */ public void registerObserver(@NonNull NetworkObserver observer, @NonNull Handler handler) { - mObservers.put(observer, handler); + if (handler == null) { + throw new IllegalArgumentException("handler must be non-null"); + } + mObservers.put(observer, Optional.of(handler)); + } + + /** + * Registers the specified observer, and start sending callbacks to it. + * + * <p>This method must only be called with callbacks that are nonblocking, such as callbacks + * that only send a message to a StateMachine. + */ + public void registerObserverForNonblockingCallback(@NonNull NetworkObserver observer) { + mObservers.put(observer, Optional.empty()); } /** @@ -77,9 +96,19 @@ public class NetworkObserverRegistry extends INetdUnsolicitedEventListener.Stub private void invokeForAllObservers(@NonNull final NetworkObserverEventCallback callback) { // ConcurrentHashMap#entrySet is weakly consistent: observers that were in the map before // creation will be processed, those added during traversal may or may not. - for (Map.Entry<NetworkObserver, Handler> entry : mObservers.entrySet()) { + for (Map.Entry<NetworkObserver, Optional<Handler>> entry : mObservers.entrySet()) { final NetworkObserver observer = entry.getKey(); - entry.getValue().post(() -> callback.sendCallback(observer)); + final Optional<Handler> handler = entry.getValue(); + if (handler.isPresent()) { + handler.get().post(() -> callback.sendCallback(observer)); + return; + } + + try { + callback.sendCallback(observer); + } catch (RuntimeException e) { + Log.e(TAG, "Error sending callback to observer", e); + } } } @@ -138,10 +167,13 @@ public class NetworkObserverRegistry extends INetdUnsolicitedEventListener.Stub @Override public void onRouteChanged(boolean updated, String route, String gateway, String ifName) { + final RouteInfo processRoute = new RouteInfo(new IpPrefix(route), + ("".equals(gateway)) ? null : InetAddresses.parseNumericAddress(gateway), + ifName); if (updated) { - invokeForAllObservers(o -> o.onRouteUpdated(route, gateway, ifName)); + invokeForAllObservers(o -> o.onRouteUpdated(processRoute)); } else { - invokeForAllObservers(o -> o.onRouteRemoved(route, gateway, ifName)); + invokeForAllObservers(o -> o.onRouteRemoved(processRoute)); } } diff --git a/packages/NetworkStack/src/com/android/server/NetworkStackService.java b/packages/NetworkStack/src/com/android/server/NetworkStackService.java index 631ee453671a..7405c471808a 100644 --- a/packages/NetworkStack/src/com/android/server/NetworkStackService.java +++ b/packages/NetworkStack/src/com/android/server/NetworkStackService.java @@ -117,7 +117,11 @@ public class NetworkStackService extends Service { mObserverRegistry = new NetworkObserverRegistry(); mCm = context.getSystemService(ConnectivityManager.class); - // TODO: call mObserverRegistry here after adding sepolicy changes + try { + mObserverRegistry.register(mNetd); + } catch (RemoteException e) { + mLog.e("Error registering observer on Netd", e); + } } @NonNull @@ -158,7 +162,7 @@ public class NetworkStackService extends Service { @Override public void makeIpClient(String ifName, IIpClientCallbacks cb) throws RemoteException { - final IpClient ipClient = new IpClient(mContext, ifName, cb); + final IpClient ipClient = new IpClient(mContext, ifName, cb, mObserverRegistry); synchronized (mIpClients) { final Iterator<WeakReference<IpClient>> it = mIpClients.iterator(); diff --git a/packages/NetworkStack/tests/src/android/net/ip/IpClientTest.java b/packages/NetworkStack/tests/src/android/net/ip/IpClientTest.java index 4ae044dec20b..7e57d1eb00b0 100644 --- a/packages/NetworkStack/tests/src/android/net/ip/IpClientTest.java +++ b/packages/NetworkStack/tests/src/android/net/ip/IpClientTest.java @@ -46,7 +46,6 @@ import android.net.RouteInfo; import android.net.shared.InitialConfiguration; import android.net.shared.ProvisioningConfiguration; import android.net.util.InterfaceParams; -import android.os.INetworkManagementService; import android.provider.Settings; import android.support.test.filters.SmallTest; import android.support.test.runner.AndroidJUnit4; @@ -54,7 +53,8 @@ import android.test.mock.MockContentResolver; import com.android.internal.R; import com.android.internal.util.test.FakeSettingsProvider; -import com.android.server.net.BaseNetworkObserver; +import com.android.server.NetworkObserver; +import com.android.server.NetworkObserverRegistry; import org.junit.Before; import org.junit.Test; @@ -87,15 +87,15 @@ public class IpClientTest { @Mock private Context mContext; @Mock private ConnectivityManager mCm; - @Mock private INetworkManagementService mNMService; + @Mock private NetworkObserverRegistry mObserverRegistry; @Mock private INetd mNetd; @Mock private Resources mResources; @Mock private IIpClientCallbacks mCb; @Mock private AlarmManager mAlarm; - @Mock private IpClient.Dependencies mDependecies; + @Mock private IpClient.Dependencies mDependencies; private MockContentResolver mContentResolver; - private BaseNetworkObserver mObserver; + private NetworkObserver mObserver; private InterfaceParams mIfParams; @Before @@ -104,6 +104,7 @@ public class IpClientTest { when(mContext.getSystemService(eq(Context.ALARM_SERVICE))).thenReturn(mAlarm); when(mContext.getSystemService(eq(ConnectivityManager.class))).thenReturn(mCm); + when(mContext.getSystemService(INetd.class)).thenReturn(mNetd); when(mContext.getResources()).thenReturn(mResources); when(mResources.getInteger(R.integer.config_networkAvoidBadWifi)) .thenReturn(DEFAULT_AVOIDBADWIFI_CONFIG_VALUE); @@ -113,28 +114,24 @@ public class IpClientTest { when(mContext.getContentResolver()).thenReturn(mContentResolver); mIfParams = null; - - when(mDependecies.getNMS()).thenReturn(mNMService); - when(mDependecies.getNetd()).thenReturn(mNetd); } private void setTestInterfaceParams(String ifname) { mIfParams = (ifname != null) ? new InterfaceParams(ifname, TEST_IFINDEX, TEST_MAC) : null; - when(mDependecies.getInterfaceParams(anyString())).thenReturn(mIfParams); + when(mDependencies.getInterfaceParams(anyString())).thenReturn(mIfParams); } private IpClient makeIpClient(String ifname) throws Exception { setTestInterfaceParams(ifname); - final IpClient ipc = new IpClient(mContext, ifname, mCb, mDependecies); + final IpClient ipc = new IpClient(mContext, ifname, mCb, mObserverRegistry, mDependencies); verify(mNetd, timeout(TEST_TIMEOUT_MS).times(1)).interfaceSetEnableIPv6(ifname, false); verify(mNetd, timeout(TEST_TIMEOUT_MS).times(1)).interfaceClearAddrs(ifname); - ArgumentCaptor<BaseNetworkObserver> arg = - ArgumentCaptor.forClass(BaseNetworkObserver.class); - verify(mNMService, times(1)).registerObserver(arg.capture()); + ArgumentCaptor<NetworkObserver> arg = ArgumentCaptor.forClass(NetworkObserver.class); + verify(mObserverRegistry, times(1)).registerObserverForNonblockingCallback(arg.capture()); mObserver = arg.getValue(); - reset(mNMService); + reset(mObserverRegistry); reset(mNetd); // Verify IpClient doesn't call onLinkPropertiesChange() when it starts. verify(mCb, never()).onLinkPropertiesChange(any()); @@ -152,7 +149,8 @@ public class IpClientTest { public void testNullInterfaceNameMostDefinitelyThrows() throws Exception { setTestInterfaceParams(null); try { - final IpClient ipc = new IpClient(mContext, null, mCb, mDependecies); + final IpClient ipc = new IpClient( + mContext, null, mCb, mObserverRegistry, mDependencies); ipc.shutdown(); fail(); } catch (NullPointerException npe) { @@ -165,7 +163,8 @@ public class IpClientTest { final String ifname = "lo"; setTestInterfaceParams(ifname); try { - final IpClient ipc = new IpClient(mContext, ifname, null, mDependecies); + final IpClient ipc = new IpClient( + mContext, ifname, null, mObserverRegistry, mDependencies); ipc.shutdown(); fail(); } catch (NullPointerException npe) { @@ -176,14 +175,16 @@ public class IpClientTest { @Test public void testInvalidInterfaceDoesNotThrow() throws Exception { setTestInterfaceParams(TEST_IFNAME); - final IpClient ipc = new IpClient(mContext, TEST_IFNAME, mCb, mDependecies); + final IpClient ipc = new IpClient( + mContext, TEST_IFNAME, mCb, mObserverRegistry, mDependencies); ipc.shutdown(); } @Test public void testInterfaceNotFoundFailsImmediately() throws Exception { setTestInterfaceParams(null); - final IpClient ipc = new IpClient(mContext, TEST_IFNAME, mCb, mDependecies); + final IpClient ipc = new IpClient( + mContext, TEST_IFNAME, mCb, mObserverRegistry, mDependencies); ipc.startProvisioning(new ProvisioningConfiguration()); verify(mCb, times(1)).onProvisioningFailure(any()); ipc.shutdown(); @@ -247,13 +248,13 @@ public class IpClientTest { // Add N - 1 addresses for (int i = 0; i < lastAddr; i++) { - mObserver.addressUpdated(iface, new LinkAddress(addresses[i])); + mObserver.onInterfaceAddressUpdated(new LinkAddress(addresses[i]), iface); verify(mCb, timeout(TEST_TIMEOUT_MS)).onLinkPropertiesChange(any()); reset(mCb); } // Add Nth address - mObserver.addressUpdated(iface, new LinkAddress(addresses[lastAddr])); + mObserver.onInterfaceAddressUpdated(new LinkAddress(addresses[lastAddr]), iface); LinkProperties want = linkproperties(links(addresses), routes(prefixes)); want.setInterfaceName(iface); verify(mCb, timeout(TEST_TIMEOUT_MS).times(1)).onProvisioningSuccess(argThat( diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java index a1aefabfc7f2..856b167b9175 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java @@ -707,17 +707,17 @@ class SettingsProtoDumpUtil { Settings.Global.GPU_DEBUG_LAYERS_GLES, GlobalSettingsProto.Gpu.DEBUG_LAYERS_GLES); dumpSetting(s, p, - Settings.Global.GUP_DEV_ALL_APPS, - GlobalSettingsProto.Gpu.GUP_DEV_ALL_APPS); + Settings.Global.GAME_DRIVER_ALL_APPS, + GlobalSettingsProto.Gpu.GAME_DRIVER_ALL_APPS); dumpSetting(s, p, - Settings.Global.GUP_DEV_OPT_IN_APPS, - GlobalSettingsProto.Gpu.GUP_DEV_OPT_IN_APPS); + Settings.Global.GAME_DRIVER_OPT_IN_APPS, + GlobalSettingsProto.Gpu.GAME_DRIVER_OPT_IN_APPS); dumpSetting(s, p, - Settings.Global.GUP_DEV_OPT_OUT_APPS, - GlobalSettingsProto.Gpu.GUP_DEV_OPT_OUT_APPS); + Settings.Global.GAME_DRIVER_OPT_OUT_APPS, + GlobalSettingsProto.Gpu.GAME_DRIVER_OPT_OUT_APPS); dumpSetting(s, p, - Settings.Global.GUP_BLACKLIST, - GlobalSettingsProto.Gpu.GUP_BLACKLIST); + Settings.Global.GAME_DRIVER_BLACKLIST, + GlobalSettingsProto.Gpu.GAME_DRIVER_BLACKLIST); dumpSetting(s, p, Settings.Global.GAME_DRIVER_WHITELIST, GlobalSettingsProto.Gpu.GAME_DRIVER_WHITELIST); diff --git a/proto/src/metrics_constants/metrics_constants.proto b/proto/src/metrics_constants/metrics_constants.proto index 43e94352a902..385931d24bee 100644 --- a/proto/src/metrics_constants/metrics_constants.proto +++ b/proto/src/metrics_constants/metrics_constants.proto @@ -6766,10 +6766,10 @@ message MetricsEvent { // OS: Q ZEN_CUSTOM_SETTINGS_DIALOG = 1612; - // OPEN: Settings > Developer Options > Game Update Packages + // OPEN: Settings > Developer Options > Game Driver Preferences // CATEGORY: SETTINGS // OS: Q - SETTINGS_GUP_DASHBOARD = 1613; + SETTINGS_GAME_DRIVER_DASHBOARD = 1613; // CATEGORY: The category for all actions relating to language detection logging. // OS: Q diff --git a/services/core/java/com/android/server/am/CoreSettingsObserver.java b/services/core/java/com/android/server/am/CoreSettingsObserver.java index 3d69aa8088f5..360d2960f61a 100644 --- a/services/core/java/com/android/server/am/CoreSettingsObserver.java +++ b/services/core/java/com/android/server/am/CoreSettingsObserver.java @@ -69,10 +69,11 @@ final class CoreSettingsObserver extends ContentObserver { sGlobalSettingToTypeMap.put(Settings.Global.GPU_DEBUG_LAYERS_GLES, String.class); sGlobalSettingToTypeMap.put(Settings.Global.GPU_DEBUG_LAYER_APP, String.class); sGlobalSettingToTypeMap.put(Settings.Global.SMS_ACCESS_RESTRICTION_ENABLED, int.class); - sGlobalSettingToTypeMap.put(Settings.Global.GUP_DEV_ALL_APPS, int.class); - sGlobalSettingToTypeMap.put(Settings.Global.GUP_DEV_OPT_IN_APPS, String.class); - sGlobalSettingToTypeMap.put(Settings.Global.GUP_DEV_OPT_OUT_APPS, String.class); - sGlobalSettingToTypeMap.put(Settings.Global.GUP_BLACKLIST, String.class); + sGlobalSettingToTypeMap.put(Settings.Global.GAME_DRIVER_ALL_APPS, int.class); + sGlobalSettingToTypeMap.put(Settings.Global.GAME_DRIVER_OPT_IN_APPS, String.class); + sGlobalSettingToTypeMap.put(Settings.Global.GAME_DRIVER_OPT_OUT_APPS, String.class); + sGlobalSettingToTypeMap.put(Settings.Global.GAME_DRIVER_BLACKLIST, String.class); + sGlobalSettingToTypeMap.put(Settings.Global.GAME_DRIVER_WHITELIST, String.class); // add other global settings here... } diff --git a/services/core/java/com/android/server/display/LogicalDisplay.java b/services/core/java/com/android/server/display/LogicalDisplay.java index 95665989cc34..7414e55ed94a 100644 --- a/services/core/java/com/android/server/display/LogicalDisplay.java +++ b/services/core/java/com/android/server/display/LogicalDisplay.java @@ -418,10 +418,15 @@ final class LogicalDisplay { // Now add back the offset for the masked area. mTempDisplayRect.offset(maskingInsets.left, maskingInsets.top); - mTempDisplayRect.left += mDisplayOffsetX; - mTempDisplayRect.right += mDisplayOffsetX; - mTempDisplayRect.top += mDisplayOffsetY; - mTempDisplayRect.bottom += mDisplayOffsetY; + if (orientation == Surface.ROTATION_0) { + mTempDisplayRect.offset(mDisplayOffsetX, mDisplayOffsetY); + } else if (orientation == Surface.ROTATION_90) { + mTempDisplayRect.offset(mDisplayOffsetY, -mDisplayOffsetX); + } else if (orientation == Surface.ROTATION_180) { + mTempDisplayRect.offset(-mDisplayOffsetX, -mDisplayOffsetY); + } else { // Surface.ROTATION_270 + mTempDisplayRect.offset(-mDisplayOffsetY, mDisplayOffsetX); + } device.setProjectionLocked(t, orientation, mTempLayerStackRect, mTempDisplayRect); } diff --git a/services/core/java/com/android/server/pm/dex/ArtManagerService.java b/services/core/java/com/android/server/pm/dex/ArtManagerService.java index 863bfd5ea391..a8be07d76b58 100644 --- a/services/core/java/com/android/server/pm/dex/ArtManagerService.java +++ b/services/core/java/com/android/server/pm/dex/ArtManagerService.java @@ -480,6 +480,10 @@ public class ArtManagerService extends android.content.pm.dex.IArtManager.Stub { final String apkPath = pkg.baseCodePath; final ApplicationInfo appInfo = pkg.applicationInfo; final String outDexFile = appInfo.dataDir + "/code_cache/compiled_view.dex"; + if (appInfo.isPrivilegedApp()) { + // Privileged apps prefer to load trusted code so they don't use compiled views. + return false; + } Log.i("PackageManager", "Compiling layouts in " + packageName + " (" + apkPath + ") to " + outDexFile); long callingId = Binder.clearCallingIdentity(); diff --git a/services/core/java/com/android/server/policy/DisplayFoldController.java b/services/core/java/com/android/server/policy/DisplayFoldController.java new file mode 100644 index 000000000000..fdcafa77a378 --- /dev/null +++ b/services/core/java/com/android/server/policy/DisplayFoldController.java @@ -0,0 +1,155 @@ +/* + * 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.policy; + +import android.content.Context; +import android.graphics.Rect; +import android.hardware.Sensor; +import android.hardware.SensorEvent; +import android.hardware.SensorEventListener; +import android.hardware.SensorManager; +import android.hardware.display.DisplayManagerInternal; +import android.os.Handler; +import android.os.RemoteCallbackList; +import android.os.RemoteException; +import android.view.DisplayInfo; +import android.view.IDisplayFoldListener; + +import com.android.server.DisplayThread; +import com.android.server.LocalServices; +import com.android.server.wm.WindowManagerInternal; + +/** + * Controls the behavior of foldable devices whose screen can literally bend and fold. + */ +class DisplayFoldController { + + private static final String TAG = "DisplayFoldController"; + private final WindowManagerInternal mWindowManagerInternal; + private final DisplayManagerInternal mDisplayManagerInternal; + private final int mDisplayId; + + /** The display area while device is folded. */ + private final Rect mFoldedArea; + private final Handler mHandler; + + private final DisplayInfo mNonOverrideDisplayInfo = new DisplayInfo(); + private final RemoteCallbackList<IDisplayFoldListener> mListeners = new RemoteCallbackList<>(); + private Boolean mFolded; + + DisplayFoldController(WindowManagerInternal windowManagerInternal, + DisplayManagerInternal displayManagerInternal, int displayId, Rect foldedArea, + Handler handler) { + mWindowManagerInternal = windowManagerInternal; + mDisplayManagerInternal = displayManagerInternal; + mDisplayId = displayId; + mFoldedArea = new Rect(foldedArea); + mHandler = handler; + } + + void requestDeviceFolded(boolean folded) { + mHandler.post(() -> setDeviceFolded(folded)); + } + + void setDeviceFolded(boolean folded) { + if (mFolded != null && mFolded == folded) { + return; + } + if (folded) { + mDisplayManagerInternal.getNonOverrideDisplayInfo(mDisplayId, mNonOverrideDisplayInfo); + final int dx = (mNonOverrideDisplayInfo.logicalWidth - mFoldedArea.width()) / 2 + - mFoldedArea.left; + final int dy = (mNonOverrideDisplayInfo.logicalHeight - mFoldedArea.height()) / 2 + - mFoldedArea.top; + + mWindowManagerInternal.setForcedDisplaySize(mDisplayId, mFoldedArea.width(), + mFoldedArea.height()); + mDisplayManagerInternal.setDisplayOffsets(mDisplayId, -dx, -dy); + } else { + mWindowManagerInternal.clearForcedDisplaySize(mDisplayId); + mDisplayManagerInternal.setDisplayOffsets(mDisplayId, 0, 0); + } + mFolded = folded; + + final int n = mListeners.beginBroadcast(); + for (int i = 0; i < n; i++) { + try { + mListeners.getBroadcastItem(i).onDisplayFoldChanged(mDisplayId, folded); + } catch (RemoteException e) { + // Listener died. + } + } + mListeners.finishBroadcast(); + } + + void registerDisplayFoldListener(IDisplayFoldListener listener) { + mListeners.register(listener); + if (mFolded == null) { + return; + } + mHandler.post(() -> { + try { + listener.onDisplayFoldChanged(mDisplayId, mFolded); + } catch (RemoteException e) { + // Listener died. + } + }); + } + + void unregisterDisplayFoldListener(IDisplayFoldListener listener) { + mListeners.unregister(listener); + } + + /** + * Only used for the case that persist.debug.force_foldable is set. + * This is using proximity sensor to simulate the fold state switch. + */ + static DisplayFoldController createWithProxSensor(Context context, int displayId) { + final SensorManager sensorManager = context.getSystemService(SensorManager.class); + final Sensor proxSensor = sensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY); + if (proxSensor == null) { + return null; + } + + final DisplayFoldController result = create(displayId); + sensorManager.registerListener(new SensorEventListener() { + @Override + public void onSensorChanged(SensorEvent event) { + result.requestDeviceFolded(event.values[0] < 1f); + } + + @Override + public void onAccuracyChanged(Sensor sensor, int accuracy) { + // Ignore. + } + }, proxSensor, SensorManager.SENSOR_DELAY_NORMAL); + + return result; + } + + static DisplayFoldController create(int displayId) { + final DisplayManagerInternal displayService = + LocalServices.getService(DisplayManagerInternal.class); + final DisplayInfo displayInfo = new DisplayInfo(); + displayService.getNonOverrideDisplayInfo(displayId, displayInfo); + final Rect foldedArea = new Rect(0, displayInfo.logicalHeight / 2, + displayInfo.logicalWidth, displayInfo.logicalHeight); + + return new DisplayFoldController(LocalServices.getService(WindowManagerInternal.class), + displayService, displayId, foldedArea, DisplayThread.getHandler()); + } +} diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java index 13c4d886e7b1..0796a9c896a3 100644 --- a/services/core/java/com/android/server/policy/PhoneWindowManager.java +++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java @@ -177,6 +177,7 @@ import android.util.SparseArray; import android.util.proto.ProtoOutputStream; import android.view.Display; import android.view.HapticFeedbackConstants; +import android.view.IDisplayFoldListener; import android.view.IWindowManager; import android.view.InputDevice; import android.view.KeyCharacterMap; @@ -374,6 +375,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { SearchManager mSearchManager; AccessibilityManager mAccessibilityManager; BurnInProtectionHelper mBurnInProtectionHelper; + private DisplayFoldController mDisplayFoldController; AppOpsManager mAppOpsManager; private ScreenshotHelper mScreenshotHelper; private boolean mHasFeatureWatch; @@ -471,6 +473,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { int mLidNavigationAccessibility; boolean mLidControlsScreenLock; boolean mLidControlsSleep; + private boolean mLidControlsDisplayFold; int mShortPressOnPowerBehavior; int mLongPressOnPowerBehavior; int mVeryLongPressOnPowerBehavior; @@ -1794,6 +1797,8 @@ public class PhoneWindowManager implements WindowManagerPolicy { com.android.internal.R.bool.config_lidControlsScreenLock); mLidControlsSleep = mContext.getResources().getBoolean( com.android.internal.R.bool.config_lidControlsSleep); + mLidControlsDisplayFold = mContext.getResources().getBoolean( + com.android.internal.R.bool.config_lidControlsDisplayFold); mAllowTheaterModeWakeFromKey = mContext.getResources().getBoolean( com.android.internal.R.bool.config_allowTheaterModeWakeFromKey); @@ -1850,6 +1855,13 @@ public class PhoneWindowManager implements WindowManagerPolicy { readConfigurationDependentBehaviors(); + if (mLidControlsDisplayFold) { + mDisplayFoldController = DisplayFoldController.create(DEFAULT_DISPLAY); + } else if (SystemProperties.getBoolean("persist.debug.force_foldable", false)) { + mDisplayFoldController = DisplayFoldController.createWithProxSensor(context, + DEFAULT_DISPLAY); + } + mAccessibilityManager = (AccessibilityManager) context.getSystemService( Context.ACCESSIBILITY_SERVICE); @@ -3194,6 +3206,20 @@ public class PhoneWindowManager implements WindowManagerPolicy { } @Override + public void registerDisplayFoldListener(IDisplayFoldListener listener) { + if (mDisplayFoldController != null) { + mDisplayFoldController.registerDisplayFoldListener(listener); + } + } + + @Override + public void unregisterDisplayFoldListener(IDisplayFoldListener listener) { + if (mDisplayFoldController != null) { + mDisplayFoldController.unregisterDisplayFoldListener(listener); + } + } + + @Override public void registerShortcutKey(long shortcutCode, IShortcutService shortcutService) throws RemoteException { synchronized (mLock) { @@ -4972,7 +4998,9 @@ public class PhoneWindowManager implements WindowManagerPolicy { private void applyLidSwitchState() { final int lidState = mDefaultDisplayPolicy.getLidState(); - if (lidState == LID_CLOSED && mLidControlsSleep) { + if (mLidControlsDisplayFold && mDisplayFoldController != null) { + mDisplayFoldController.requestDeviceFolded(lidState == LID_CLOSED); + } else if (lidState == LID_CLOSED && mLidControlsSleep) { goToSleep(SystemClock.uptimeMillis(), PowerManager.GO_TO_SLEEP_REASON_LID_SWITCH, PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE); } else if (lidState == LID_CLOSED && mLidControlsScreenLock) { diff --git a/services/core/java/com/android/server/policy/WindowManagerPolicy.java b/services/core/java/com/android/server/policy/WindowManagerPolicy.java index 1d829707f180..e18cd179b113 100644 --- a/services/core/java/com/android/server/policy/WindowManagerPolicy.java +++ b/services/core/java/com/android/server/policy/WindowManagerPolicy.java @@ -78,6 +78,7 @@ import android.util.Slog; import android.util.proto.ProtoOutputStream; import android.view.Display; import android.view.IApplicationToken; +import android.view.IDisplayFoldListener; import android.view.IWindowManager; import android.view.InputEventReceiver; import android.view.KeyEvent; @@ -1457,6 +1458,16 @@ public interface WindowManagerPolicy extends WindowManagerPolicyConstants { public void requestUserActivityNotification(); /** + * Registers an IDisplayFoldListener. + */ + default void registerDisplayFoldListener(IDisplayFoldListener listener) {} + + /** + * Unregisters an IDisplayFoldListener. + */ + default void unregisterDisplayFoldListener(IDisplayFoldListener listener) {} + + /** * Updates the flag about whether AOD is showing. * * @return whether the value was changed. diff --git a/services/core/java/com/android/server/wm/WindowManagerInternal.java b/services/core/java/com/android/server/wm/WindowManagerInternal.java index e204697e46cf..33e46f4af301 100644 --- a/services/core/java/com/android/server/wm/WindowManagerInternal.java +++ b/services/core/java/com/android/server/wm/WindowManagerInternal.java @@ -313,6 +313,22 @@ public abstract class WindowManagerInternal { public abstract void waitForAllWindowsDrawn(Runnable callback, long timeout); /** + * Overrides the display size. + * + * @param displayId The display to override the display size. + * @param width The width to override. + * @param height The height to override. + */ + public abstract void setForcedDisplaySize(int displayId, int width, int height); + + /** + * Recover the display size to real display size. + * + * @param displayId The display to recover the display size. + */ + public abstract void clearForcedDisplaySize(int displayId); + + /** * Adds a window token for a given window type. * * @param token The token to add. diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index e6581df233ef..8373b44112fe 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -192,6 +192,7 @@ import android.view.DisplayCutout; import android.view.DisplayInfo; import android.view.Gravity; import android.view.IAppTransitionAnimationSpecsFuture; +import android.view.IDisplayFoldListener; import android.view.IDockedStackListener; import android.view.IInputFilter; import android.view.IOnKeyguardExitResult; @@ -3748,6 +3749,16 @@ public class WindowManagerService extends IWindowManager.Stub } @Override + public void registerDisplayFoldListener(IDisplayFoldListener listener) { + mPolicy.registerDisplayFoldListener(listener); + } + + @Override + public void unregisterDisplayFoldListener(IDisplayFoldListener listener) { + mPolicy.unregisterDisplayFoldListener(listener); + } + + @Override public int getPreferredOptionsPanelGravity(int displayId) { synchronized (mGlobalLock) { final DisplayContent displayContent = mRoot.getDisplayContent(displayId); @@ -6937,6 +6948,16 @@ public class WindowManagerService extends IWindowManager.Stub } @Override + public void setForcedDisplaySize(int displayId, int width, int height) { + WindowManagerService.this.setForcedDisplaySize(displayId, width, height); + } + + @Override + public void clearForcedDisplaySize(int displayId) { + WindowManagerService.this.clearForcedDisplaySize(displayId); + } + + @Override public void addWindowToken(IBinder token, int type, int displayId) { WindowManagerService.this.addWindowToken(token, type, displayId); } diff --git a/services/core/java/com/android/server/wm/utils/WmDisplayCutout.java b/services/core/java/com/android/server/wm/utils/WmDisplayCutout.java index ea3f758fb209..98bad93e09e2 100644 --- a/services/core/java/com/android/server/wm/utils/WmDisplayCutout.java +++ b/services/core/java/com/android/server/wm/utils/WmDisplayCutout.java @@ -170,4 +170,9 @@ public class WmDisplayCutout { public int hashCode() { return Objects.hash(mInner, mFrameSize); } + + @Override + public String toString() { + return "WmDisplayCutout{" + mInner + ", mFrameSize=" + mFrameSize + '}'; + } } diff --git a/telephony/java/com/android/ims/internal/uce/common/StatusCode.java b/telephony/java/com/android/ims/internal/uce/common/StatusCode.java index 3921cfbbfce7..7250eee70ecb 100644 --- a/telephony/java/com/android/ims/internal/uce/common/StatusCode.java +++ b/telephony/java/com/android/ims/internal/uce/common/StatusCode.java @@ -64,6 +64,10 @@ public class StatusCode implements Parcelable { public static final int UCE_NO_CHANGE_IN_CAP = 13; /** Service is unknown. */ public static final int UCE_SERVICE_UNKNOWN = 14; + /** Service cannot support Invalid Feature Tag */ + public static final int UCE_INVALID_FEATURE_TAG = 15; + /** Service is Available */ + public static final int UCE_SERVICE_AVAILABLE = 16; private int mStatusCode = UCE_SUCCESS; diff --git a/telephony/java/com/android/ims/internal/uce/uceservice/IUceService.aidl b/telephony/java/com/android/ims/internal/uce/uceservice/IUceService.aidl index 43f83cd94d57..1fb8513d410a 100644 --- a/telephony/java/com/android/ims/internal/uce/uceservice/IUceService.aidl +++ b/telephony/java/com/android/ims/internal/uce/uceservice/IUceService.aidl @@ -66,10 +66,30 @@ interface IUceService * service the client created. * * @return optionsServiceHandle + * * @hide + * + * @deprecated This is replaced with new API createOptionsServiceForSubscription() */ int createOptionsService(IOptionsListener optionsListener, inout UceLong optionsServiceListenerHdl); + /** + * Creates a options service for Capability Discovery. + * @param optionsListener IOptionsListener object. + * @param optionsServiceListenerHdl wrapper for client's listener handle to be stored. + * @param iccId the ICC-ID derived from SubscriptionInfo for the Service requested + * + * The service will fill UceLong.mUceLong with presenceListenerHandle allocated and + * used to validate callbacks received in IPresenceListener are indeed from the + * service the client created. + * + * @return optionsServiceHandle + * + * @hide + */ + int createOptionsServiceForSubscription(IOptionsListener optionsListener, + inout UceLong optionsServiceListenerHdl, + in String iccId); /** * Destroys a Options service. @@ -89,14 +109,36 @@ interface IUceService * service the client created. * * @return presenceServiceHdl + * * @hide + * + * @deprecated This is replaced with new API createPresenceServiceForSubscription() */ int createPresenceService(IPresenceListener presenceServiceListener, inout UceLong presenceServiceListenerHdl); + /** + * Creates a presence service. + * @param presenceServiceListener IPresenceListener object + * @param presenceServiceListenerHdl wrapper for client's listener handle to be stored. + * @param iccId the ICC-ID derived from SubscriptionInfo for the Service requested + * + * The service will fill UceLong.mUceLong with presenceListenerHandle allocated and + * used to validate callbacks received in IPresenceListener are indeed from the + * service the client created. + * + * @return presenceServiceHdl + * + * @hide + */ + int createPresenceServiceForSubscription(IPresenceListener presenceServiceListener, + inout UceLong presenceServiceListenerHdl, + in String iccId); /** * Destroys a presence service. + * * @param presenceServiceHdl handle returned during createPresenceService() + * * @hide */ void destroyPresenceService(int presenceServiceHdl); @@ -105,23 +147,55 @@ interface IUceService /** * Query the UCE Service for information to know whether the is registered. + * * @return boolean, true if Registered to for network events else false. + * * @hide */ boolean getServiceStatus(); /** * Query the UCE Service for presence Service. + * * @return IPresenceService object. + * * @hide + * + * @deprecated use API getPresenceServiceForSubscription() */ IPresenceService getPresenceService(); /** + * Query the UCE Service for presence Service. + * + * @param iccId the ICC-ID derived from SubscriptionInfo for the Service requested + * + * @return IPresenceService object. + * + * @hide + */ + IPresenceService getPresenceServiceForSubscription(in String iccId); + + /** * Query the UCE Service for options service object. + * * @return IOptionsService object. + * + * @deprecated use API getOptionsServiceForSubscription() + * * @hide */ IOptionsService getOptionsService(); + /** + * Query the UCE Service for options service object. + * + * @param iccId the ICC-ID derived from SubscriptionInfo for the Service requested + * + * @return IOptionsService object. + * + * @hide + */ + IOptionsService getOptionsServiceForSubscription(in String iccId); + } diff --git a/telephony/java/com/android/ims/internal/uce/uceservice/UceServiceBase.java b/telephony/java/com/android/ims/internal/uce/uceservice/UceServiceBase.java index 3660e039582d..ceb191910427 100644 --- a/telephony/java/com/android/ims/internal/uce/uceservice/UceServiceBase.java +++ b/telephony/java/com/android/ims/internal/uce/uceservice/UceServiceBase.java @@ -56,6 +56,14 @@ public abstract class UceServiceBase { return onCreateOptionsService(optionsListener, optionsServiceListenerHdl); } + @Override + public int createOptionsServiceForSubscription(IOptionsListener optionsListener, + UceLong optionsServiceListenerHdl, + String iccId) { + return onCreateOptionsService(optionsListener, optionsServiceListenerHdl, + iccId); + } + @Override public void destroyOptionsService(int optionsServiceHandle) { @@ -70,6 +78,14 @@ public abstract class UceServiceBase { } @Override + public int createPresenceServiceForSubscription(IPresenceListener presServiceListener, + UceLong presServiceListenerHdl, + String iccId) { + return onCreatePresService(presServiceListener, presServiceListenerHdl, + iccId); + } + + @Override public void destroyPresenceService(int presServiceHdl) { onDestroyPresService(presServiceHdl); } @@ -85,9 +101,19 @@ public abstract class UceServiceBase { } @Override + public IPresenceService getPresenceServiceForSubscription(String iccId) { + return onGetPresenceService(iccId); + } + + @Override public IOptionsService getOptionsService() { return onGetOptionsService(); } + + @Override + public IOptionsService getOptionsServiceForSubscription(String iccId) { + return onGetOptionsService(iccId); + } } private UceServiceBinder mBinder; @@ -120,6 +146,13 @@ public abstract class UceServiceBase { return 0; } + protected int onCreateOptionsService(IOptionsListener optionsListener, + UceLong optionsServiceListenerHdl, + String iccId) { + //no-op + return 0; + } + protected void onDestroyOptionsService(int cdServiceHandle) { //no-op return; @@ -131,6 +164,13 @@ public abstract class UceServiceBase { return 0; } + protected int onCreatePresService(IPresenceListener presServiceListener, + UceLong presServiceListenerHdl, + String iccId) { + //no-op + return 0; + } + protected void onDestroyPresService(int presServiceHdl) { //no-op return; @@ -146,8 +186,18 @@ public abstract class UceServiceBase { return null; } + protected IPresenceService onGetPresenceService(String iccId) { + //no-op + return null; + } + protected IOptionsService onGetOptionsService () { //no-op return null; } + + protected IOptionsService onGetOptionsService (String iccId) { + //no-op + return null; + } } |