diff options
139 files changed, 4949 insertions, 1680 deletions
diff --git a/api/current.txt b/api/current.txt index ab3909b25ae4..8090aa02c960 100644 --- a/api/current.txt +++ b/api/current.txt @@ -6468,7 +6468,6 @@ package android.app.admin { method public java.lang.CharSequence getOrganizationName(android.content.ComponentName); method public java.util.List<android.telephony.data.ApnSetting> getOverrideApns(android.content.ComponentName); method public android.app.admin.DevicePolicyManager getParentProfileInstance(android.content.ComponentName); - method public java.lang.String getPasswordBlacklistName(android.content.ComponentName); method public long getPasswordExpiration(android.content.ComponentName); method public long getPasswordExpirationTimeout(android.content.ComponentName); method public int getPasswordHistoryLength(android.content.ComponentName); @@ -6577,7 +6576,6 @@ package android.app.admin { method public void setOrganizationName(android.content.ComponentName, java.lang.CharSequence); method public void setOverrideApnsEnabled(android.content.ComponentName, boolean); method public java.lang.String[] setPackagesSuspended(android.content.ComponentName, java.lang.String[], boolean); - method public boolean setPasswordBlacklist(android.content.ComponentName, java.lang.String, java.util.List<java.lang.String>); method public void setPasswordExpirationTimeout(android.content.ComponentName, long); method public void setPasswordHistoryLength(android.content.ComponentName, int); method public void setPasswordMinimumLength(android.content.ComponentName, int); @@ -7306,6 +7304,7 @@ package android.app.slice { } public abstract class SliceProvider extends android.content.ContentProvider { + ctor public SliceProvider(java.lang.String...); ctor public SliceProvider(); method public final int delete(android.net.Uri, java.lang.String, java.lang.String[]); method public final java.lang.String getType(android.net.Uri); @@ -13651,17 +13650,17 @@ package android.graphics { method public boolean isDecodeAsAlphaMaskEnabled(); method public boolean isMutableRequired(); method public boolean isUnpremultipliedRequired(); - method public android.graphics.ImageDecoder setAllocator(int); - method public android.graphics.ImageDecoder setConserveMemory(boolean); - method public android.graphics.ImageDecoder setCrop(android.graphics.Rect); - method public android.graphics.ImageDecoder setDecodeAsAlphaMaskEnabled(boolean); - method public android.graphics.ImageDecoder setMutableRequired(boolean); - method public android.graphics.ImageDecoder setOnPartialImageListener(android.graphics.ImageDecoder.OnPartialImageListener); - method public android.graphics.ImageDecoder setPostProcessor(android.graphics.PostProcessor); - method public android.graphics.ImageDecoder setTargetColorSpace(android.graphics.ColorSpace); - method public android.graphics.ImageDecoder setTargetSampleSize(int); - method public android.graphics.ImageDecoder setTargetSize(int, int); - method public android.graphics.ImageDecoder setUnpremultipliedRequired(boolean); + method public void setAllocator(int); + method public void setConserveMemory(boolean); + method public void setCrop(android.graphics.Rect); + method public void setDecodeAsAlphaMaskEnabled(boolean); + method public void setMutableRequired(boolean); + method public void setOnPartialImageListener(android.graphics.ImageDecoder.OnPartialImageListener); + method public void setPostProcessor(android.graphics.PostProcessor); + method public void setTargetColorSpace(android.graphics.ColorSpace); + method public void setTargetSampleSize(int); + method public void setTargetSize(int, int); + method public void setUnpremultipliedRequired(boolean); field public static final int ALLOCATOR_DEFAULT = 0; // 0x0 field public static final int ALLOCATOR_HARDWARE = 3; // 0x3 field public static final int ALLOCATOR_SHARED_MEMORY = 2; // 0x2 @@ -21965,6 +21964,7 @@ package android.media { } public final class AudioDeviceInfo { + method public java.lang.String getAddress(); method public int[] getChannelCounts(); method public int[] getChannelIndexMasks(); method public int[] getChannelMasks(); @@ -24456,6 +24456,7 @@ package android.media { } public final class MicrophoneInfo { + method public java.lang.String getAddress(); method public java.util.List<android.util.Pair<java.lang.Integer, java.lang.Integer>> getChannelMapping(); method public java.lang.String getDescription(); method public int getDirectionality(); @@ -27242,6 +27243,8 @@ package android.net { public class NetworkRequest implements android.os.Parcelable { method public int describeContents(); + method public boolean hasCapability(int); + method public boolean hasTransport(int); method public void writeToParcel(android.os.Parcel, int); field public static final android.os.Parcelable.Creator<android.net.NetworkRequest> CREATOR; } @@ -38564,6 +38567,7 @@ package android.security.keystore { method public boolean isRandomizedEncryptionRequired(); method public boolean isStrongBoxBacked(); method public boolean isTrustedUserPresenceRequired(); + method public boolean isUnlockedDeviceRequired(); method public boolean isUserAuthenticationRequired(); method public boolean isUserAuthenticationValidWhileOnBody(); method public boolean isUserConfirmationRequired(); @@ -38591,6 +38595,7 @@ package android.security.keystore { method public android.security.keystore.KeyGenParameterSpec.Builder setRandomizedEncryptionRequired(boolean); method public android.security.keystore.KeyGenParameterSpec.Builder setSignaturePaddings(java.lang.String...); method public android.security.keystore.KeyGenParameterSpec.Builder setTrustedUserPresenceRequired(boolean); + method public android.security.keystore.KeyGenParameterSpec.Builder setUnlockedDeviceRequired(boolean); method public android.security.keystore.KeyGenParameterSpec.Builder setUserAuthenticationRequired(boolean); method public android.security.keystore.KeyGenParameterSpec.Builder setUserAuthenticationValidWhileOnBody(boolean); method public android.security.keystore.KeyGenParameterSpec.Builder setUserAuthenticationValidityDurationSeconds(int); @@ -38683,6 +38688,7 @@ package android.security.keystore { method public boolean isInvalidatedByBiometricEnrollment(); method public boolean isRandomizedEncryptionRequired(); method public boolean isTrustedUserPresenceRequired(); + method public boolean isUnlockedDeviceRequired(); method public boolean isUserAuthenticationRequired(); method public boolean isUserAuthenticationValidWhileOnBody(); method public boolean isUserConfirmationRequired(); @@ -38702,6 +38708,7 @@ package android.security.keystore { method public android.security.keystore.KeyProtection.Builder setRandomizedEncryptionRequired(boolean); method public android.security.keystore.KeyProtection.Builder setSignaturePaddings(java.lang.String...); method public android.security.keystore.KeyProtection.Builder setTrustedUserPresenceRequired(boolean); + method public android.security.keystore.KeyProtection.Builder setUnlockedDeviceRequired(boolean); method public android.security.keystore.KeyProtection.Builder setUserAuthenticationRequired(boolean); method public android.security.keystore.KeyProtection.Builder setUserAuthenticationValidWhileOnBody(boolean); method public android.security.keystore.KeyProtection.Builder setUserAuthenticationValidityDurationSeconds(int); diff --git a/api/system-current.txt b/api/system-current.txt index ed763babb7ef..1284de88a91d 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -3105,6 +3105,10 @@ package android.net { field public static final int ERROR_INVALID_NETWORK = 1; // 0x1 } + public final class NetworkCapabilities implements android.os.Parcelable { + field public static final int NET_CAPABILITY_OEM_PAID = 22; // 0x16 + } + public class NetworkKey implements android.os.Parcelable { ctor public NetworkKey(android.net.WifiKey); method public int describeContents(); diff --git a/cmds/incidentd/src/Section.cpp b/cmds/incidentd/src/Section.cpp index 3b57d34c1ea3..d964651aab3a 100644 --- a/cmds/incidentd/src/Section.cpp +++ b/cmds/incidentd/src/Section.cpp @@ -319,8 +319,10 @@ status_t GZipSection::Execute(ReportRequestSet* requests) const { index++; // look at the next file. } VLOG("GZipSection is using file %s, fd=%d", mFilenames[index], fd.get()); - if (fd.get() == -1) return -1; - + if (fd.get() == -1) { + ALOGW("GZipSection %s can't open all the files", this->name.string()); + return NO_ERROR; // e.g. LAST_KMSG will reach here in user build. + } FdBuffer buffer; Fpipe p2cPipe; Fpipe c2pPipe; diff --git a/cmds/incidentd/tests/PrivacyBuffer_test.cpp b/cmds/incidentd/tests/PrivacyBuffer_test.cpp index 5edc0c79785b..d12926993d0b 100644 --- a/cmds/incidentd/tests/PrivacyBuffer_test.cpp +++ b/cmds/incidentd/tests/PrivacyBuffer_test.cpp @@ -38,7 +38,7 @@ const uint8_t STRING_TYPE = 9; const uint8_t MESSAGE_TYPE = 11; const string STRING_FIELD_0 = "\x02\viamtestdata"; const string VARINT_FIELD_1 = "\x08\x96\x01"; // 150 -const string STRING_FIELD_2 = "\x12\vwhatthefuck"; +const string STRING_FIELD_2 = "\x12\vandroidwins"; const string FIX64_FIELD_3 = "\x19\xff\xff\xff\xff\xff\xff\xff\xff"; // -1 const string FIX32_FIELD_4 = "\x25\xff\xff\xff\xff"; // -1 const string MESSAGE_FIELD_5 = "\x2a\x10" + VARINT_FIELD_1 + STRING_FIELD_2; @@ -274,4 +274,4 @@ TEST_F(PrivacyBufferTest, AutoMessage) { autoMsg->children = list; string expected = "\x2a\xd" + STRING_FIELD_2; assertStripByFields(DEST_AUTOMATIC, expected, 1, autoMsg); -}
\ No newline at end of file +} diff --git a/cmds/incidentd/tests/Section_test.cpp b/cmds/incidentd/tests/Section_test.cpp index f93839b62dcf..2f6698bc3116 100644 --- a/cmds/incidentd/tests/Section_test.cpp +++ b/cmds/incidentd/tests/Section_test.cpp @@ -19,6 +19,7 @@ #include <android-base/file.h> #include <android-base/test_utils.h> #include <android/os/IncidentReportArgs.h> +#include <android/util/protobuf.h> #include <frameworks/base/libs/incident/proto/android/os/header.pb.h> #include <gmock/gmock.h> #include <gtest/gtest.h> @@ -31,12 +32,13 @@ const int REVERSE_PARSER = 1; const int QUICK_TIMEOUT_MS = 100; const string VARINT_FIELD_1 = "\x08\x96\x01"; // 150 -const string STRING_FIELD_2 = "\x12\vwhatthefuck"; +const string STRING_FIELD_2 = "\x12\vandroidwins"; const string FIX64_FIELD_3 = "\x19\xff\xff\xff\xff\xff\xff\xff\xff"; // -1 using namespace android::base; using namespace android::binder; using namespace android::os; +using namespace android::util; using namespace std; using ::testing::StrEq; using ::testing::Test; @@ -154,17 +156,26 @@ TEST_F(SectionTest, GZipSection) { requests.setMainDest(android::os::DEST_LOCAL); ASSERT_EQ(NO_ERROR, gs.Execute(&requests)); - std::string expect, gzFile, actual; + std::string expected, gzFile, actual; ASSERT_TRUE(ReadFileToString(testGzFile, &gzFile)); ASSERT_TRUE(ReadFileToString(tf.path, &actual)); - expect = "\x2\xC6\x6\n\"" + testFile + "\x12\x9F\x6" + gzFile; - EXPECT_THAT(actual, StrEq(expect)); + // generates the expected protobuf result. + size_t fileLen = testFile.size(); + size_t totalLen = 1 + get_varint_size(fileLen) + fileLen + 3 + gzFile.size(); + uint8_t header[20]; + header[0] = '\x2'; // header 0 << 3 + 2 + uint8_t* ptr = write_raw_varint(header + 1, totalLen); + *ptr = '\n'; // header 1 << 3 + 2 + ptr = write_raw_varint(++ptr, fileLen); + expected.assign((const char*)header, ptr - header); + expected += testFile + "\x12\x9F\x6" + gzFile; + EXPECT_THAT(actual, StrEq(expected)); } TEST_F(SectionTest, GZipSectionNoFileFound) { GZipSection gs(NOOP_PARSER, "/tmp/nonexist1", "/tmp/nonexist2", NULL); requests.setMainFd(STDOUT_FILENO); - ASSERT_EQ(-1, gs.Execute(&requests)); + ASSERT_EQ(NO_ERROR, gs.Execute(&requests)); } TEST_F(SectionTest, CommandSectionConstructor) { diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.cpp b/cmds/statsd/src/metrics/ValueMetricProducer.cpp index bd3c78ce839d..d0f510df2aa2 100644 --- a/cmds/statsd/src/metrics/ValueMetricProducer.cpp +++ b/cmds/statsd/src/metrics/ValueMetricProducer.cpp @@ -312,8 +312,13 @@ void ValueMetricProducer::onMatchedLogEventInternalLocked( if (mPullTagId != -1) { // for pulled events if (mCondition == true) { - interval.start = value; - interval.startUpdated = true; + if (!interval.startUpdated) { + interval.start = value; + interval.startUpdated = true; + } else { + // skip it if there is already value recorded for the start + VLOG("Already recorded value for this dimension %s", eventKey.toString().c_str()); + } } else { // Generally we expect value to be monotonically increasing. // If not, there was a reset event. We take the absolute value as @@ -382,6 +387,7 @@ void ValueMetricProducer::flushCurrentBucketLocked(const uint64_t& eventTimeNs) int tainted = 0; for (const auto& slice : mCurrentSlicedBucket) { tainted += slice.second.tainted; + tainted += slice.second.startUpdated; info.mValue = slice.second.sum; // it will auto create new vector of ValuebucketInfo if the key is not found. auto& bucketList = mPastBuckets[slice.first]; diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.h b/cmds/statsd/src/metrics/ValueMetricProducer.h index ebc6e81d22c9..45d95316e8e1 100644 --- a/cmds/statsd/src/metrics/ValueMetricProducer.h +++ b/cmds/statsd/src/metrics/ValueMetricProducer.h @@ -159,6 +159,10 @@ private: FRIEND_TEST(ValueMetricProducerTest, TestPulledValueWithUpgrade); FRIEND_TEST(ValueMetricProducerTest, TestPushedEventsWithoutCondition); FRIEND_TEST(ValueMetricProducerTest, TestAnomalyDetection); + FRIEND_TEST(ValueMetricProducerTest, TestBucketBoundaryNoCondition); + FRIEND_TEST(ValueMetricProducerTest, TestBucketBoundaryWithCondition); + FRIEND_TEST(ValueMetricProducerTest, TestBucketBoundaryWithCondition2); + FRIEND_TEST(ValueMetricProducerTest, TestBucketBoundaryWithCondition3); }; } // namespace statsd diff --git a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp index a0224ec17a78..c650a06fdbe1 100644 --- a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp +++ b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp @@ -45,6 +45,8 @@ const int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000L const int64_t bucket2StartTimeNs = bucketStartTimeNs + bucketSizeNs; const int64_t bucket3StartTimeNs = bucketStartTimeNs + 2 * bucketSizeNs; const int64_t bucket4StartTimeNs = bucketStartTimeNs + 3 * bucketSizeNs; +const int64_t bucket5StartTimeNs = bucketStartTimeNs + 4 * bucketSizeNs; +const int64_t bucket6StartTimeNs = bucketStartTimeNs + 5 * bucketSizeNs; const int64_t eventUpgradeTimeNs = bucketStartTimeNs + 15 * NS_PER_SEC; /* @@ -431,6 +433,376 @@ TEST(ValueMetricProducerTest, TestAnomalyDetection) { std::ceil(1.0 * event6->GetElapsedTimestampNs() / NS_PER_SEC + refPeriodSec)); } +// Test value metric no condition, the pull on bucket boundary come in time and too late +TEST(ValueMetricProducerTest, TestBucketBoundaryNoCondition) { + 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); + + sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); + shared_ptr<MockStatsPullerManager> pullerManager = + make_shared<StrictMock<MockStatsPullerManager>>(); + EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return()); + EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillOnce(Return()); + + ValueMetricProducer valueProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard, + tagId, bucketStartTimeNs, pullerManager); + valueProducer.setBucketSize(60 * NS_PER_SEC); + + vector<shared_ptr<LogEvent>> allData; + // pull 1 + allData.clear(); + shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 1); + event->write(tagId); + event->write(11); + event->init(); + allData.push_back(event); + + valueProducer.onDataPulled(allData); + // has one slice + EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size()); + ValueMetricProducer::Interval curInterval = valueProducer.mCurrentSlicedBucket.begin()->second; + valueProducer.setBucketSize(60 * NS_PER_SEC); + + // startUpdated:true tainted:0 sum:0 start:11 + EXPECT_EQ(true, curInterval.startUpdated); + EXPECT_EQ(0, curInterval.tainted); + EXPECT_EQ(0, curInterval.sum); + EXPECT_EQ(11, curInterval.start); + EXPECT_EQ(1UL, valueProducer.mPastBuckets.size()); + EXPECT_EQ(0, valueProducer.mPastBuckets.begin()->second.back().mValue); + + // pull 2 at correct time + allData.clear(); + event = make_shared<LogEvent>(tagId, bucket3StartTimeNs + 1); + event->write(tagId); + event->write(23); + event->init(); + allData.push_back(event); + valueProducer.onDataPulled(allData); + // has one slice + EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size()); + curInterval = valueProducer.mCurrentSlicedBucket.begin()->second; + // tartUpdated:false tainted:0 sum:12 + EXPECT_EQ(true, curInterval.startUpdated); + EXPECT_EQ(0, curInterval.tainted); + EXPECT_EQ(0, curInterval.sum); + EXPECT_EQ(1UL, valueProducer.mPastBuckets.size()); + EXPECT_EQ(2UL, valueProducer.mPastBuckets.begin()->second.size()); + EXPECT_EQ(12, valueProducer.mPastBuckets.begin()->second.back().mValue); + + // pull 3 come late. + // The previous bucket gets closed with error. (Has start value 23, no ending) + // Another bucket gets closed with error. (No start, but ending with 36) + // The new bucket is back to normal. + allData.clear(); + event = make_shared<LogEvent>(tagId, bucket6StartTimeNs + 1); + event->write(tagId); + event->write(36); + event->init(); + allData.push_back(event); + valueProducer.onDataPulled(allData); + EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size()); + curInterval = valueProducer.mCurrentSlicedBucket.begin()->second; + // startUpdated:false tainted:0 sum:12 + EXPECT_EQ(true, curInterval.startUpdated); + EXPECT_EQ(0, curInterval.tainted); + EXPECT_EQ(36, curInterval.start); + EXPECT_EQ(0, curInterval.sum); + EXPECT_EQ(1UL, valueProducer.mPastBuckets.size()); + EXPECT_EQ(4UL, valueProducer.mPastBuckets.begin()->second.size()); + EXPECT_EQ(0, valueProducer.mPastBuckets.begin()->second[0].mValue); + EXPECT_EQ(12, valueProducer.mPastBuckets.begin()->second[1].mValue); + EXPECT_EQ(0, valueProducer.mPastBuckets.begin()->second[2].mValue); + EXPECT_EQ(0, valueProducer.mPastBuckets.begin()->second[3].mValue); +} + +/* + * Test pulled event with non sliced condition. The pull on boundary come late because the alarm + * was delivered late. + */ +TEST(ValueMetricProducerTest, TestBucketBoundaryWithCondition) { + 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.set_condition(StringToId("SCREEN_ON")); + + sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); + shared_ptr<MockStatsPullerManager> pullerManager = + make_shared<StrictMock<MockStatsPullerManager>>(); + EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return()); + EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillRepeatedly(Return()); + + EXPECT_CALL(*pullerManager, Pull(tagId, _, _)) + // condition becomes true + .WillOnce(Invoke([](int tagId, int64_t timeNs, + vector<std::shared_ptr<LogEvent>>* data) { + data->clear(); + shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10); + event->write(tagId); + event->write(100); + event->init(); + data->push_back(event); + return true; + })) + // condition becomes false + .WillOnce(Invoke([](int tagId, int64_t timeNs, + vector<std::shared_ptr<LogEvent>>* data) { + data->clear(); + shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 20); + event->write(tagId); + event->write(120); + event->init(); + data->push_back(event); + return true; + })); + + ValueMetricProducer valueProducer(kConfigKey, metric, 1, wizard, tagId, bucketStartTimeNs, + pullerManager); + valueProducer.setBucketSize(60 * NS_PER_SEC); + valueProducer.onConditionChanged(true, bucketStartTimeNs + 8); + + // has one slice + EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size()); + ValueMetricProducer::Interval curInterval = valueProducer.mCurrentSlicedBucket.begin()->second; + // startUpdated:false tainted:0 sum:0 start:100 + EXPECT_EQ(100, curInterval.start); + EXPECT_EQ(true, curInterval.startUpdated); + EXPECT_EQ(0, curInterval.tainted); + EXPECT_EQ(0, curInterval.sum); + EXPECT_EQ(0UL, valueProducer.mPastBuckets.size()); + + // pull on bucket boundary come late, condition change happens before it + valueProducer.onConditionChanged(false, bucket2StartTimeNs + 1); + curInterval = valueProducer.mCurrentSlicedBucket.begin()->second; + EXPECT_EQ(false, curInterval.startUpdated); + EXPECT_EQ(1, curInterval.tainted); + EXPECT_EQ(0, curInterval.sum); + EXPECT_EQ(1UL, valueProducer.mPastBuckets.size()); + EXPECT_EQ(1UL, valueProducer.mPastBuckets.begin()->second.size()); + EXPECT_EQ(0, valueProducer.mPastBuckets.begin()->second[0].mValue); + + // Now the alarm is delivered. + // since the condition turned to off before this pull finish, it has no effect + vector<shared_ptr<LogEvent>> allData; + allData.clear(); + shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 30); + event->write(1); + event->write(110); + event->init(); + allData.push_back(event); + valueProducer.onDataPulled(allData); + + curInterval = valueProducer.mCurrentSlicedBucket.begin()->second; + EXPECT_EQ(false, curInterval.startUpdated); + EXPECT_EQ(1, curInterval.tainted); + EXPECT_EQ(0, curInterval.sum); + EXPECT_EQ(1UL, valueProducer.mPastBuckets.size()); + EXPECT_EQ(1UL, valueProducer.mPastBuckets.begin()->second.size()); + EXPECT_EQ(0, valueProducer.mPastBuckets.begin()->second[0].mValue); +} + +/* + * Test pulled event with non sliced condition. The pull on boundary come late, after the condition + * change to false, and then true again. This is due to alarm delivered late. + */ +TEST(ValueMetricProducerTest, TestBucketBoundaryWithCondition2) { + 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.set_condition(StringToId("SCREEN_ON")); + + sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); + shared_ptr<MockStatsPullerManager> pullerManager = + make_shared<StrictMock<MockStatsPullerManager>>(); + EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillRepeatedly(Return()); + EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillRepeatedly(Return()); + + EXPECT_CALL(*pullerManager, Pull(tagId, _, _)) + // condition becomes true + .WillOnce(Invoke([](int tagId, int64_t timeNs, + vector<std::shared_ptr<LogEvent>>* data) { + data->clear(); + shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10); + event->write(tagId); + event->write(100); + event->init(); + data->push_back(event); + return true; + })) + // condition becomes false + .WillOnce(Invoke([](int tagId, int64_t timeNs, + vector<std::shared_ptr<LogEvent>>* data) { + data->clear(); + shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 20); + event->write(tagId); + event->write(120); + event->init(); + data->push_back(event); + return true; + })) + // condition becomes true again + .WillOnce(Invoke([](int tagId, int64_t timeNs, + vector<std::shared_ptr<LogEvent>>* data) { + data->clear(); + shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 30); + event->write(tagId); + event->write(130); + event->init(); + data->push_back(event); + return true; + })); + + ValueMetricProducer valueProducer(kConfigKey, metric, 1, wizard, tagId, bucketStartTimeNs, + pullerManager); + valueProducer.setBucketSize(60 * NS_PER_SEC); + valueProducer.onConditionChanged(true, bucketStartTimeNs + 8); + + // has one slice + EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size()); + ValueMetricProducer::Interval curInterval = valueProducer.mCurrentSlicedBucket.begin()->second; + // startUpdated:false tainted:0 sum:0 start:100 + EXPECT_EQ(100, curInterval.start); + EXPECT_EQ(true, curInterval.startUpdated); + EXPECT_EQ(0, curInterval.tainted); + EXPECT_EQ(0, curInterval.sum); + EXPECT_EQ(0UL, valueProducer.mPastBuckets.size()); + + // pull on bucket boundary come late, condition change happens before it + valueProducer.onConditionChanged(false, bucket2StartTimeNs + 1); + curInterval = valueProducer.mCurrentSlicedBucket.begin()->second; + EXPECT_EQ(false, curInterval.startUpdated); + EXPECT_EQ(1, curInterval.tainted); + EXPECT_EQ(0, curInterval.sum); + EXPECT_EQ(1UL, valueProducer.mPastBuckets.size()); + EXPECT_EQ(1UL, valueProducer.mPastBuckets.begin()->second.size()); + EXPECT_EQ(0, valueProducer.mPastBuckets.begin()->second[0].mValue); + + // condition changed to true again, before the pull alarm is delivered + valueProducer.onConditionChanged(true, bucket2StartTimeNs + 25); + curInterval = valueProducer.mCurrentSlicedBucket.begin()->second; + EXPECT_EQ(true, curInterval.startUpdated); + EXPECT_EQ(130, curInterval.start); + EXPECT_EQ(1, curInterval.tainted); + EXPECT_EQ(0, curInterval.sum); + EXPECT_EQ(1UL, valueProducer.mPastBuckets.size()); + EXPECT_EQ(1UL, valueProducer.mPastBuckets.begin()->second.size()); + EXPECT_EQ(0, valueProducer.mPastBuckets.begin()->second[0].mValue); + + // Now the alarm is delivered, but it is considered late, it has no effect + vector<shared_ptr<LogEvent>> allData; + allData.clear(); + shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 50); + event->write(1); + event->write(110); + event->init(); + allData.push_back(event); + valueProducer.onDataPulled(allData); + + curInterval = valueProducer.mCurrentSlicedBucket.begin()->second; + EXPECT_EQ(true, curInterval.startUpdated); + EXPECT_EQ(130, curInterval.start); + EXPECT_EQ(1, curInterval.tainted); + EXPECT_EQ(0, curInterval.sum); + EXPECT_EQ(1UL, valueProducer.mPastBuckets.size()); + EXPECT_EQ(1UL, valueProducer.mPastBuckets.begin()->second.size()); + EXPECT_EQ(0, valueProducer.mPastBuckets.begin()->second[0].mValue); +} + +/* + * Test pulled event with non sliced condition. The pull on boundary come late because the puller is + * very slow. + */ +TEST(ValueMetricProducerTest, TestBucketBoundaryWithCondition3) { + 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.set_condition(StringToId("SCREEN_ON")); + + sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); + shared_ptr<MockStatsPullerManager> pullerManager = + make_shared<StrictMock<MockStatsPullerManager>>(); + EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _, _)).WillOnce(Return()); + EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillRepeatedly(Return()); + + EXPECT_CALL(*pullerManager, Pull(tagId, _, _)) + // condition becomes true + .WillOnce(Invoke([](int tagId, int64_t timeNs, + vector<std::shared_ptr<LogEvent>>* data) { + data->clear(); + shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10); + event->write(tagId); + event->write(100); + event->init(); + data->push_back(event); + return true; + })) + // condition becomes false + .WillOnce(Invoke([](int tagId, int64_t timeNs, + vector<std::shared_ptr<LogEvent>>* data) { + data->clear(); + shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket3StartTimeNs + 20); + event->write(tagId); + event->write(120); + event->init(); + data->push_back(event); + return true; + })); + + ValueMetricProducer valueProducer(kConfigKey, metric, 1, wizard, tagId, bucketStartTimeNs, + pullerManager); + valueProducer.setBucketSize(60 * NS_PER_SEC); + valueProducer.onConditionChanged(true, bucketStartTimeNs + 8); + + // has one slice + EXPECT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size()); + ValueMetricProducer::Interval curInterval = valueProducer.mCurrentSlicedBucket.begin()->second; + // startUpdated:false tainted:0 sum:0 start:100 + EXPECT_EQ(100, curInterval.start); + EXPECT_EQ(true, curInterval.startUpdated); + EXPECT_EQ(0, curInterval.tainted); + EXPECT_EQ(0, curInterval.sum); + EXPECT_EQ(0UL, valueProducer.mPastBuckets.size()); + + // pull on bucket boundary come late, condition change happens before it. + // But puller is very slow in this one, so the data come after bucket finish + valueProducer.onConditionChanged(false, bucket2StartTimeNs + 1); + curInterval = valueProducer.mCurrentSlicedBucket.begin()->second; + EXPECT_EQ(false, curInterval.startUpdated); + EXPECT_EQ(1, curInterval.tainted); + EXPECT_EQ(0, curInterval.sum); + EXPECT_EQ(1UL, valueProducer.mPastBuckets.size()); + EXPECT_EQ(1UL, valueProducer.mPastBuckets.begin()->second.size()); + EXPECT_EQ(0, valueProducer.mPastBuckets.begin()->second[0].mValue); + + // Alarm is delivered in time, but the pull is very slow, and pullers are called in order, + // so this one comes even later + vector<shared_ptr<LogEvent>> allData; + allData.clear(); + shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket3StartTimeNs + 30); + event->write(1); + event->write(110); + event->init(); + allData.push_back(event); + valueProducer.onDataPulled(allData); + + curInterval = valueProducer.mCurrentSlicedBucket.begin()->second; + EXPECT_EQ(false, curInterval.startUpdated); + EXPECT_EQ(1, curInterval.tainted); + EXPECT_EQ(0, curInterval.sum); + EXPECT_EQ(1UL, valueProducer.mPastBuckets.size()); + EXPECT_EQ(1UL, valueProducer.mPastBuckets.begin()->second.size()); + EXPECT_EQ(0, valueProducer.mPastBuckets.begin()->second[0].mValue); +} + } // namespace statsd } // namespace os } // namespace android diff --git a/config/hiddenapi-light-greylist.txt b/config/hiddenapi-light-greylist.txt index 70e3fade244f..015c4b664bc3 100644 --- a/config/hiddenapi-light-greylist.txt +++ b/config/hiddenapi-light-greylist.txt @@ -7,6 +7,7 @@ Landroid/animation/ValueAnimator;->animateValue(F)V Landroid/animation/ValueAnimator;->sDurationScale:F Landroid/app/Activity;->getActivityOptions()Landroid/app/ActivityOptions; Landroid/app/Activity;->getActivityToken()Landroid/os/IBinder; +Landroid/app/ActivityGroup;->mLocalActivityManager:Landroid/app/LocalActivityManager; Landroid/app/Activity;->mActivityInfo:Landroid/content/pm/ActivityInfo; Landroid/app/ActivityManager;->clearApplicationUserData(Ljava/lang/String;Landroid/content/pm/IPackageDataObserver;)Z Landroid/app/ActivityManager;->getMaxRecentTasksStatic()I @@ -63,8 +64,10 @@ Landroid/app/ActivityThread$ActivityClientRecord;->token:Landroid/os/IBinder; Landroid/app/ActivityThread$AppBindData;->appInfo:Landroid/content/pm/ApplicationInfo; Landroid/app/ActivityThread$AppBindData;->info:Landroid/app/LoadedApk; Landroid/app/ActivityThread$AppBindData;->instrumentationArgs:Landroid/os/Bundle; +Landroid/app/ActivityThread$AppBindData;->persistent:Z Landroid/app/ActivityThread$AppBindData;->processName:Ljava/lang/String; Landroid/app/ActivityThread$AppBindData;->providers:Ljava/util/List; +Landroid/app/ActivityThread$AppBindData;->restrictedBackupMode:Z Landroid/app/ActivityThread$BindServiceData;->intent:Landroid/content/Intent; Landroid/app/ActivityThread$BindServiceData;->token:Landroid/os/IBinder; Landroid/app/ActivityThread$CreateServiceData;->compatInfo:Landroid/content/res/CompatibilityInfo; @@ -89,11 +92,13 @@ Landroid/app/ActivityThread;->handleBindApplication(Landroid/app/ActivityThread$ Landroid/app/ActivityThread$H;->BIND_SERVICE:I Landroid/app/ActivityThread$H;->CREATE_SERVICE:I Landroid/app/ActivityThread$H;->DUMP_PROVIDER:I +Landroid/app/ActivityThread$H;->ENTER_ANIMATION_COMPLETE:I Landroid/app/ActivityThread$H;->EXIT_APPLICATION:I Landroid/app/ActivityThread$H;->GC_WHEN_IDLE:I Landroid/app/ActivityThread$H;->INSTALL_PROVIDER:I Landroid/app/ActivityThread$H;->RECEIVER:I Landroid/app/ActivityThread$H;->REMOVE_PROVIDER:I +Landroid/app/ActivityThread$H;->SCHEDULE_CRASH:I Landroid/app/ActivityThread$H;->SERVICE_ARGS:I Landroid/app/ActivityThread$H;->STOP_SERVICE:I Landroid/app/ActivityThread$H;->UNBIND_SERVICE:I @@ -102,6 +107,7 @@ Landroid/app/ActivityThread;->installProvider(Landroid/content/Context;Landroid/ Landroid/app/ActivityThread;->mActivities:Landroid/util/ArrayMap; Landroid/app/ActivityThread;->mAllApplications:Ljava/util/ArrayList; Landroid/app/ActivityThread;->mBoundApplication:Landroid/app/ActivityThread$AppBindData; +Landroid/app/ActivityThread;->mConfiguration:Landroid/content/res/Configuration; Landroid/app/ActivityThread;->mDensityCompatMode:Z Landroid/app/ActivityThread;->mH:Landroid/app/ActivityThread$H; Landroid/app/ActivityThread;->mInitialApplication:Landroid/app/Application; @@ -110,8 +116,10 @@ Landroid/app/ActivityThread;->mLocalProvidersByName:Landroid/util/ArrayMap; Landroid/app/ActivityThread;->mLocalProviders:Landroid/util/ArrayMap; Landroid/app/ActivityThread;->mNumVisibleActivities:I Landroid/app/ActivityThread;->mPackages:Landroid/util/ArrayMap; +Landroid/app/ActivityThread;->mPendingConfiguration:Landroid/content/res/Configuration; Landroid/app/ActivityThread;->mProviderMap:Landroid/util/ArrayMap; Landroid/app/ActivityThread;->mResourcePackages:Landroid/util/ArrayMap; +Landroid/app/ActivityThread;->mResourcesManager:Landroid/app/ResourcesManager; Landroid/app/ActivityThread;->mServices:Landroid/util/ArrayMap; Landroid/app/ActivityThread;->performNewIntents(Landroid/os/IBinder;Ljava/util/List;Z)V Landroid/app/ActivityThread;->performStopActivity(Landroid/os/IBinder;ZLjava/lang/String;)V @@ -146,6 +154,7 @@ Landroid/app/AlarmManager;->WINDOW_HEURISTIC:J Landroid/app/AlertDialog$Builder;->P:Lcom/android/internal/app/AlertController$AlertParams; Landroid/app/AlertDialog;->mAlert:Lcom/android/internal/app/AlertController; Landroid/app/AppGlobals;->getInitialApplication()Landroid/app/Application; +Landroid/app/AppGlobals;->getInitialPackage()Ljava/lang/String; Landroid/app/AppGlobals;->getPackageManager()Landroid/content/pm/IPackageManager; Landroid/app/Application;->attach(Landroid/content/Context;)V Landroid/app/Application;->collectActivityLifecycleCallbacks()[Ljava/lang/Object; @@ -166,21 +175,26 @@ Landroid/app/ApplicationPackageManager;->getPackageCurrentVolume(Landroid/conten Landroid/app/ApplicationPackageManager;->getPackageSizeInfoAsUser(Ljava/lang/String;ILandroid/content/pm/IPackageStatsObserver;)V Landroid/app/ApplicationPackageManager;-><init>(Landroid/app/ContextImpl;Landroid/content/pm/IPackageManager;)V Landroid/app/ApplicationPackageManager;->mPM:Landroid/content/pm/IPackageManager; +Landroid/app/ApplicationPackageManager;->shouldShowRequestPermissionRationale(Ljava/lang/String;)Z Landroid/app/AppOpsManager;->checkOp(IILjava/lang/String;)I Landroid/app/AppOpsManager;->checkOpNoThrow(IILjava/lang/String;)I Landroid/app/AppOpsManager;->getOpsForPackage(ILjava/lang/String;[I)Ljava/util/List; Landroid/app/AppOpsManager;->mService:Lcom/android/internal/app/IAppOpsService; Landroid/app/AppOpsManager;->noteOp(I)I Landroid/app/AppOpsManager;->noteOp(IILjava/lang/String;)I +Landroid/app/AppOpsManager;->OP_AUDIO_NOTIFICATION_VOLUME:I Landroid/app/AppOpsManager;->OP_COARSE_LOCATION:I Landroid/app/AppOpsManager$OpEntry;->getDuration()I Landroid/app/AppOpsManager$OpEntry;->getRejectTime()J Landroid/app/AppOpsManager;->OP_FINE_LOCATION:I Landroid/app/AppOpsManager;->OP_GET_USAGE_STATS:I Landroid/app/AppOpsManager;->OP_POST_NOTIFICATION:I +Landroid/app/AppOpsManager;->OP_READ_CONTACTS:I Landroid/app/AppOpsManager;->OP_READ_PHONE_STATE:I Landroid/app/AppOpsManager;->OP_READ_SMS:I +Landroid/app/AppOpsManager;->OP_VIBRATE:I Landroid/app/AppOpsManager;->OP_WIFI_SCAN:I +Landroid/app/AppOpsManager;->OP_WRITE_CONTACTS:I Landroid/app/AppOpsManager;->OP_WRITE_SMS:I Landroid/app/AppOpsManager;->permissionToOpCode(Ljava/lang/String;)I Landroid/app/AppOpsManager;->strOpToOp(Ljava/lang/String;)I @@ -204,6 +218,7 @@ Landroid/app/ContextImpl;->getDisplay()Landroid/view/Display; Landroid/app/ContextImpl;->getPreferencesDir()Ljava/io/File; Landroid/app/ContextImpl;->getReceiverRestrictedContext()Landroid/content/Context; Landroid/app/ContextImpl;->mBasePackageName:Ljava/lang/String; +Landroid/app/ContextImpl;->mClassLoader:Ljava/lang/ClassLoader; Landroid/app/ContextImpl;->mContentResolver:Landroid/app/ContextImpl$ApplicationContentResolver; Landroid/app/ContextImpl;->mMainThread:Landroid/app/ActivityThread; Landroid/app/ContextImpl;->mOpPackageName:Ljava/lang/String; @@ -213,6 +228,7 @@ Landroid/app/ContextImpl;->mPackageManager:Landroid/content/pm/PackageManager; Landroid/app/ContextImpl;->mResources:Landroid/content/res/Resources; Landroid/app/ContextImpl;->mServiceCache:[Ljava/lang/Object; Landroid/app/ContextImpl;->mTheme:Landroid/content/res/Resources$Theme; +Landroid/app/ContextImpl;->mThemeResource:I Landroid/app/ContextImpl;->scheduleFinalCleanup(Ljava/lang/String;Ljava/lang/String;)V Landroid/app/ContextImpl;->setOuterContext(Landroid/content/Context;)V Landroid/app/ContextImpl;->sSharedPrefsCache:Landroid/util/ArrayMap; @@ -230,10 +246,13 @@ Landroid/app/FragmentManagerImpl;->noteStateNotSaved()V Landroid/app/Fragment;->mChildFragmentManager:Landroid/app/FragmentManagerImpl; Landroid/app/Fragment;->mWho:Ljava/lang/String; Landroid/app/IActivityManager;->bindService(Landroid/app/IApplicationThread;Landroid/os/IBinder;Landroid/content/Intent;Ljava/lang/String;Landroid/app/IServiceConnection;ILjava/lang/String;I)I +Landroid/app/IActivityManager;->broadcastIntent(Landroid/app/IApplicationThread;Landroid/content/Intent;Ljava/lang/String;Landroid/content/IIntentReceiver;ILjava/lang/String;Landroid/os/Bundle;[Ljava/lang/String;ILandroid/os/Bundle;ZZI)I Landroid/app/IActivityManager;->finishActivity(Landroid/os/IBinder;ILandroid/content/Intent;I)Z Landroid/app/IActivityManager;->finishReceiver(Landroid/os/IBinder;ILjava/lang/String;Landroid/os/Bundle;ZI)V Landroid/app/IActivityManager;->forceStopPackage(Ljava/lang/String;I)V +Landroid/app/IActivityManager;->getIntentSender(ILjava/lang/String;Landroid/os/IBinder;Ljava/lang/String;I[Landroid/content/Intent;[Ljava/lang/String;ILandroid/os/Bundle;I)Landroid/content/IIntentSender; Landroid/app/IActivityManager;->getLaunchedFromPackage(Landroid/os/IBinder;)Ljava/lang/String; +Landroid/app/IActivityManager;->getProviderMimeType(Landroid/net/Uri;I)Ljava/lang/String; Landroid/app/IActivityManager;->getTaskForActivity(Landroid/os/IBinder;Z)I Landroid/app/IActivityManager;->moveTaskToFront(IILandroid/os/Bundle;)V Landroid/app/IActivityManager;->publishContentProviders(Landroid/app/IApplicationThread;Ljava/util/List;)V @@ -241,16 +260,23 @@ Landroid/app/IActivityManager;->requestBugReport(I)V Landroid/app/IActivityManager;->resumeAppSwitches()V Landroid/app/IActivityManager;->setRequestedOrientation(Landroid/os/IBinder;I)V Landroid/app/IActivityManager;->setTaskResizeable(II)V +Landroid/app/IActivityManager;->stopService(Landroid/app/IApplicationThread;Landroid/content/Intent;Ljava/lang/String;I)I Landroid/app/IActivityManager$Stub$Proxy;->getConfiguration()Landroid/content/res/Configuration; Landroid/app/IActivityManager$Stub$Proxy;->getLaunchedFromUid(Landroid/os/IBinder;)I +Landroid/app/IActivityManager$Stub$Proxy;->getProcessLimit()I Landroid/app/IActivityManager$Stub$Proxy;->getProcessPss([I)[J Landroid/app/IActivityManager$Stub$Proxy;->isAppForeground(I)Z Landroid/app/IActivityManager$Stub$Proxy;->mRemote:Landroid/os/IBinder; Landroid/app/IActivityManager;->unbindService(Landroid/app/IServiceConnection;)Z +Landroid/app/IActivityManager;->unstableProviderDied(Landroid/os/IBinder;)V Landroid/app/IAlarmManager$Stub$Proxy;-><init>(Landroid/os/IBinder;)V Landroid/app/IAlarmManager$Stub;->TRANSACTION_remove:I Landroid/app/IAlarmManager$Stub;->TRANSACTION_set:I Landroid/app/IApplicationThread;->scheduleTrimMemory(I)V +Landroid/app/INotificationManager;->cancelAllNotifications(Ljava/lang/String;I)V +Landroid/app/INotificationManager;->cancelNotificationWithTag(Ljava/lang/String;Ljava/lang/String;II)V +Landroid/app/INotificationManager;->cancelToast(Ljava/lang/String;Landroid/app/ITransientNotification;)V +Landroid/app/INotificationManager;->enqueueToast(Ljava/lang/String;Landroid/app/ITransientNotification;I)V Landroid/app/INotificationManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/app/INotificationManager; Landroid/app/INotificationManager$Stub$Proxy;-><init>(Landroid/os/IBinder;)V Landroid/app/Instrumentation;->execStartActivities(Landroid/content/Context;Landroid/os/IBinder;Landroid/os/IBinder;Landroid/app/Activity;[Landroid/content/Intent;Landroid/os/Bundle;)V @@ -258,9 +284,12 @@ Landroid/app/Instrumentation;->execStartActivity(Landroid/content/Context;Landro Landroid/app/Instrumentation;->execStartActivity(Landroid/content/Context;Landroid/os/IBinder;Landroid/os/IBinder;Ljava/lang/String;Landroid/content/Intent;ILandroid/os/Bundle;)Landroid/app/Instrumentation$ActivityResult; Landroid/app/Instrumentation;->execStartActivity(Landroid/content/Context;Landroid/os/IBinder;Landroid/os/IBinder;Ljava/lang/String;Landroid/content/Intent;ILandroid/os/Bundle;Landroid/os/UserHandle;)Landroid/app/Instrumentation$ActivityResult; Landroid/app/IntentService;->mServiceHandler:Landroid/app/IntentService$ServiceHandler; +Landroid/app/IProcessObserver$Stub;-><init>()V Landroid/app/ISearchManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/app/ISearchManager; Landroid/app/ISearchManager$Stub$Proxy;->getGlobalSearchActivity()Landroid/content/ComponentName; Landroid/app/ISearchManager$Stub$Proxy;->getWebSearchActivity()Landroid/content/ComponentName; +Landroid/app/IServiceConnection$Stub;->asInterface(Landroid/os/IBinder;)Landroid/app/IServiceConnection; +Landroid/app/IStopUserCallback;->userStopped(I)V Landroid/app/IUiModeManager$Stub$Proxy;-><init>(Landroid/os/IBinder;)V Landroid/app/IWallpaperManager;->getWallpaper(Ljava/lang/String;Landroid/app/IWallpaperManagerCallback;ILandroid/os/Bundle;I)Landroid/os/ParcelFileDescriptor; Landroid/app/job/IJobScheduler$Stub;->asInterface(Landroid/os/IBinder;)Landroid/app/job/IJobScheduler; @@ -287,16 +316,21 @@ Landroid/app/LoadedApk;->mSplitResDirs:[Ljava/lang/String; Landroid/app/LoadedApk;->rewriteRValues(Ljava/lang/ClassLoader;Ljava/lang/String;I)V Landroid/app/LocalActivityManager;->mActivities:Ljava/util/Map; Landroid/app/LocalActivityManager;->mActivityArray:Ljava/util/ArrayList; +Landroid/app/LocalActivityManager;->mParent:Landroid/app/Activity; +Landroid/app/LocalActivityManager;->mResumed:Landroid/app/LocalActivityManager$LocalActivityRecord; +Landroid/app/LocalActivityManager;->mSingleMode:Z Landroid/app/NativeActivity;->hideIme(I)V Landroid/app/NativeActivity;->setWindowFlags(II)V Landroid/app/NativeActivity;->setWindowFormat(I)V Landroid/app/NativeActivity;->showIme(I)V Landroid/app/Notification$Builder;->mActions:Ljava/util/ArrayList; +Landroid/app/Notification$Builder;->makePublicContentView()Landroid/widget/RemoteViews; Landroid/app/Notification$Builder;->setChannel(Ljava/lang/String;)Landroid/app/Notification$Builder; Landroid/app/Notification;->isGroupSummary()Z Landroid/app/NotificationManager;->getService()Landroid/app/INotificationManager; Landroid/app/NotificationManager;->notifyAsUser(Ljava/lang/String;ILandroid/app/Notification;Landroid/os/UserHandle;)V Landroid/app/NotificationManager;->sService:Landroid/app/INotificationManager; +Landroid/app/Notification;->mChannelId:Ljava/lang/String; Landroid/app/Notification;->mGroupKey:Ljava/lang/String; Landroid/app/Notification;->mLargeIcon:Landroid/graphics/drawable/Icon; Landroid/app/Notification;->mSmallIcon:Landroid/graphics/drawable/Icon; @@ -309,6 +343,7 @@ Landroid/app/Presentation;->createPresentationContext(Landroid/content/Context;L Landroid/app/ProgressDialog;->mProgressNumber:Landroid/widget/TextView; Landroid/app/QueuedWork;->addFinisher(Ljava/lang/Runnable;)V Landroid/app/QueuedWork;->removeFinisher(Ljava/lang/Runnable;)V +Landroid/app/QueuedWork;->sFinishers:Ljava/util/LinkedList; Landroid/app/ResourcesManager;->appendLibAssetForMainAssetPath(Ljava/lang/String;Ljava/lang/String;)V Landroid/app/ResourcesManager;->getInstance()Landroid/app/ResourcesManager; Landroid/app/ResourcesManager;->mActivityResourceReferences:Ljava/util/WeakHashMap; @@ -322,7 +357,9 @@ Landroid/app/Service;->mStartCompatibility:Z Landroid/app/Service;->mThread:Landroid/app/ActivityThread; Landroid/app/Service;->mToken:Landroid/os/IBinder; Landroid/app/Service;->setForeground(Z)V +Landroid/app/SharedPreferencesImpl;-><init>(Ljava/io/File;I)V Landroid/app/SharedPreferencesImpl;->mFile:Ljava/io/File; +Landroid/app/SharedPreferencesImpl;->startReloadIfChangedUnexpectedly()V Landroid/app/StatusBarManager;->collapsePanels()V Landroid/app/StatusBarManager;->disable(I)V Landroid/app/StatusBarManager;->expandNotificationsPanel()V @@ -429,6 +466,7 @@ Landroid/content/IContentService$Stub;->asInterface(Landroid/os/IBinder;)Landroi Landroid/content/IContentService$Stub$Proxy;-><init>(Landroid/os/IBinder;)V Landroid/content/Intent;->ACTION_ALARM_CHANGED:Ljava/lang/String; Landroid/content/IntentFilter;->mActions:Ljava/util/ArrayList; +Landroid/content/Intent;->mExtras:Landroid/os/Bundle; Landroid/content/Intent;->putExtra(Ljava/lang/String;Landroid/os/IBinder;)Landroid/content/Intent; Landroid/content/pm/ActivityInfo;->resizeMode:I Landroid/content/pm/ApplicationInfo;->enabledSetting:I @@ -439,18 +477,33 @@ Landroid/content/pm/ApplicationInfo;->primaryCpuAbi:Ljava/lang/String; Landroid/content/pm/ApplicationInfo;->privateFlags:I Landroid/content/pm/ApplicationInfo;->scanPublicSourceDir:Ljava/lang/String; Landroid/content/pm/ApplicationInfo;->scanSourceDir:Ljava/lang/String; +Landroid/content/pm/ApplicationInfo;->secondaryCpuAbi:Ljava/lang/String; Landroid/content/pm/ApplicationInfo;->secondaryNativeLibraryDir:Ljava/lang/String; Landroid/content/pm/ComponentInfo;->getComponentName()Landroid/content/ComponentName; +Landroid/content/pm/IPackageDataObserver$Stub;->asInterface(Landroid/os/IBinder;)Landroid/content/pm/IPackageDataObserver; +Landroid/content/pm/IPackageManager;->addPermissionAsync(Landroid/content/pm/PermissionInfo;)Z +Landroid/content/pm/IPackageManager;->addPermission(Landroid/content/pm/PermissionInfo;)Z +Landroid/content/pm/IPackageManager;->getComponentEnabledSetting(Landroid/content/ComponentName;I)I +Landroid/content/pm/IPackageManager;->getInstalledPackages(II)Landroid/content/pm/ParceledListSlice; +Landroid/content/pm/IPackageManager;->getInstallerPackageName(Ljava/lang/String;)Ljava/lang/String; Landroid/content/pm/IPackageManager;->getInstallLocation()I Landroid/content/pm/IPackageManager;->getLastChosenActivity(Landroid/content/Intent;Ljava/lang/String;I)Landroid/content/pm/ResolveInfo; +Landroid/content/pm/IPackageManager;->getProviderInfo(Landroid/content/ComponentName;II)Landroid/content/pm/ProviderInfo; +Landroid/content/pm/IPackageManager;->getReceiverInfo(Landroid/content/ComponentName;II)Landroid/content/pm/ActivityInfo; +Landroid/content/pm/IPackageManager;->getServiceInfo(Landroid/content/ComponentName;II)Landroid/content/pm/ServiceInfo; Landroid/content/pm/IPackageManager;->setApplicationEnabledSetting(Ljava/lang/String;IIILjava/lang/String;)V Landroid/content/pm/IPackageManager;->setComponentEnabledSetting(Landroid/content/ComponentName;III)V +Landroid/content/pm/IPackageManager;->setInstallerPackageName(Ljava/lang/String;Ljava/lang/String;)V Landroid/content/pm/IPackageManager;->setLastChosenActivity(Landroid/content/Intent;Ljava/lang/String;ILandroid/content/IntentFilter;ILandroid/content/ComponentName;)V Landroid/content/pm/IPackageManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/content/pm/IPackageManager; +Landroid/content/pm/IPackageManager$Stub$Proxy;->getInstalledPackages(II)Landroid/content/pm/ParceledListSlice; Landroid/content/pm/IPackageManager$Stub$Proxy;->getPackageInfo(Ljava/lang/String;II)Landroid/content/pm/PackageInfo; Landroid/content/pm/IPackageManager$Stub$Proxy;->getPackagesForUid(I)[Ljava/lang/String; Landroid/content/pm/IPackageManager$Stub$Proxy;->getSystemSharedLibraryNames()[Ljava/lang/String; +Landroid/content/pm/IPackageMoveObserver$Stub;->asInterface(Landroid/os/IBinder;)Landroid/content/pm/IPackageMoveObserver; +Landroid/content/pm/IPackageMoveObserver$Stub;-><init>()V Landroid/content/pm/IPackageStatsObserver$Stub;-><init>()V +Landroid/content/pm/IShortcutService$Stub$Proxy;-><init>(Landroid/os/IBinder;)V Landroid/content/pm/LauncherActivityInfo;->mActivityInfo:Landroid/content/pm/ActivityInfo; Landroid/content/pm/LauncherApps;->mPm:Landroid/content/pm/PackageManager; Landroid/content/pm/LauncherApps;->startShortcut(Ljava/lang/String;Ljava/lang/String;Landroid/graphics/Rect;Landroid/os/Bundle;I)V @@ -475,14 +528,29 @@ Landroid/content/pm/PackageParser;->collectCertificates(Landroid/content/pm/Pack Landroid/content/pm/PackageParser$Component;->className:Ljava/lang/String; Landroid/content/pm/PackageParser$Component;->getComponentName()Landroid/content/ComponentName; Landroid/content/pm/PackageParser$Component;->intents:Ljava/util/ArrayList; +Landroid/content/pm/PackageParser;->generateActivityInfo(Landroid/content/pm/PackageParser$Activity;ILandroid/content/pm/PackageUserState;I)Landroid/content/pm/ActivityInfo; Landroid/content/pm/PackageParser;->generatePackageInfo(Landroid/content/pm/PackageParser$Package;[IIJJLjava/util/Set;Landroid/content/pm/PackageUserState;I)Landroid/content/pm/PackageInfo; Landroid/content/pm/PackageParser;->generatePackageInfo(Landroid/content/pm/PackageParser$Package;[IIJJLjava/util/Set;Landroid/content/pm/PackageUserState;)Landroid/content/pm/PackageInfo; +Landroid/content/pm/PackageParser;->generateProviderInfo(Landroid/content/pm/PackageParser$Provider;ILandroid/content/pm/PackageUserState;I)Landroid/content/pm/ProviderInfo; +Landroid/content/pm/PackageParser;->generateServiceInfo(Landroid/content/pm/PackageParser$Service;ILandroid/content/pm/PackageUserState;I)Landroid/content/pm/ServiceInfo; Landroid/content/pm/PackageParser;-><init>()V +Landroid/content/pm/PackageParser$Instrumentation;->info:Landroid/content/pm/InstrumentationInfo; +Landroid/content/pm/PackageParser$IntentInfo;->banner:I +Landroid/content/pm/PackageParser$IntentInfo;->hasDefault:Z +Landroid/content/pm/PackageParser$IntentInfo;->icon:I +Landroid/content/pm/PackageParser$IntentInfo;-><init>()V +Landroid/content/pm/PackageParser$IntentInfo;->labelRes:I +Landroid/content/pm/PackageParser$IntentInfo;->logo:I +Landroid/content/pm/PackageParser$IntentInfo;->nonLocalizedLabel:Ljava/lang/CharSequence; Landroid/content/pm/PackageParser$Package;->activities:Ljava/util/ArrayList; Landroid/content/pm/PackageParser$Package;->applicationInfo:Landroid/content/pm/ApplicationInfo; +Landroid/content/pm/PackageParser$Package;->instrumentation:Ljava/util/ArrayList; +Landroid/content/pm/PackageParser$Package;->mAppMetaData:Landroid/os/Bundle; Landroid/content/pm/PackageParser$Package;->mVersionCode:I Landroid/content/pm/PackageParser$Package;->mVersionName:Ljava/lang/String; Landroid/content/pm/PackageParser$Package;->packageName:Ljava/lang/String; +Landroid/content/pm/PackageParser$Package;->permissionGroups:Ljava/util/ArrayList; +Landroid/content/pm/PackageParser$Package;->permissions:Ljava/util/ArrayList; Landroid/content/pm/PackageParser$Package;->providers:Ljava/util/ArrayList; Landroid/content/pm/PackageParser$Package;->receivers:Ljava/util/ArrayList; Landroid/content/pm/PackageParser$Package;->requestedPermissions:Ljava/util/ArrayList; @@ -493,6 +561,7 @@ Landroid/content/pm/PackageParser;->parsePackage(Ljava/io/File;I)Landroid/conten Landroid/content/pm/PackageParser;->parsePackage(Ljava/io/File;IZ)Landroid/content/pm/PackageParser$Package; Landroid/content/pm/PackageParser$Provider;->info:Landroid/content/pm/ProviderInfo; Landroid/content/pm/PackageParser$ProviderIntentInfo;->provider:Landroid/content/pm/PackageParser$Provider; +Landroid/content/pm/PackageParser$Service;->info:Landroid/content/pm/ServiceInfo; Landroid/content/pm/PackageParser$ServiceIntentInfo;->service:Landroid/content/pm/PackageParser$Service; Landroid/content/pm/PackageUserState;-><init>()V Landroid/content/pm/ParceledListSlice;-><init>(Ljava/util/List;)V @@ -531,14 +600,18 @@ Landroid/content/res/ColorStateList;->mFactory:Landroid/content/res/ColorStateLi Landroid/content/res/CompatibilityInfo;->applicationScale:F Landroid/content/res/CompatibilityInfo;->DEFAULT_COMPATIBILITY_INFO:Landroid/content/res/CompatibilityInfo; Landroid/content/res/DrawableCache;->getInstance(JLandroid/content/res/Resources;Landroid/content/res/Resources$Theme;)Landroid/graphics/drawable/Drawable; +Landroid/content/res/DrawableCache;-><init>()V Landroid/content/res/ObbInfo;->salt:[B Landroid/content/res/Resources;->getCompatibilityInfo()Landroid/content/res/CompatibilityInfo; +Landroid/content/res/ResourcesImpl;->getAssets()Landroid/content/res/AssetManager; Landroid/content/res/ResourcesImpl;->mAccessLock:Ljava/lang/Object; +Landroid/content/res/ResourcesImpl;->mAnimatorCache:Landroid/content/res/ConfigurationBoundResourceCache; Landroid/content/res/ResourcesImpl;->mAssets:Landroid/content/res/AssetManager; Landroid/content/res/ResourcesImpl;->mColorDrawableCache:Landroid/content/res/DrawableCache; Landroid/content/res/ResourcesImpl;->mConfiguration:Landroid/content/res/Configuration; Landroid/content/res/ResourcesImpl;->mDrawableCache:Landroid/content/res/DrawableCache; Landroid/content/res/ResourcesImpl;->mPreloading:Z +Landroid/content/res/ResourcesImpl;->mStateListAnimatorCache:Landroid/content/res/ConfigurationBoundResourceCache; Landroid/content/res/ResourcesImpl;->sPreloadedColorDrawables:Landroid/util/LongSparseArray; Landroid/content/res/ResourcesImpl;->sPreloadedComplexColors:Landroid/util/LongSparseArray; Landroid/content/res/ResourcesImpl;->sPreloadedDrawables:[Landroid/util/LongSparseArray; @@ -554,6 +627,7 @@ Landroid/content/res/Resources;->mTypedArrayPool:Landroid/util/Pools$Synchronize Landroid/content/res/Resources;->setCompatibilityInfo(Landroid/content/res/CompatibilityInfo;)V Landroid/content/res/Resources;->updateSystemConfiguration(Landroid/content/res/Configuration;Landroid/util/DisplayMetrics;Landroid/content/res/CompatibilityInfo;)V Landroid/content/res/StringBlock;-><init>(JZ)V +Landroid/content/res/ThemedResourceCache;->onConfigurationChange(I)V Landroid/content/res/TypedArray;->extractThemeAttrs()[I Landroid/content/res/TypedArray;->getNonConfigurationString(II)Ljava/lang/String; Landroid/content/res/TypedArray;->getValueAt(ILandroid/util/TypedValue;)Z @@ -572,7 +646,10 @@ Landroid/content/res/XmlBlock;->newParser()Landroid/content/res/XmlResourceParse Landroid/content/res/XmlBlock$Parser;->mBlock:Landroid/content/res/XmlBlock; Landroid/content/res/XmlBlock$Parser;->mParseState:J Landroid/content/SearchRecentSuggestionsProvider;->mSuggestionProjection:[Ljava/lang/String; +Landroid/content/SyncContext;->setStatusText(Ljava/lang/String;)V Landroid/content/SyncStatusInfo;->lastSuccessTime:J +Landroid/content/UriMatcher;->mChildren:Ljava/util/ArrayList; +Landroid/content/UriMatcher;->mText:Ljava/lang/String; Landroid/database/AbstractCursor;->mExtras:Landroid/os/Bundle; Landroid/database/AbstractCursor;->mNotifyUri:Landroid/net/Uri; Landroid/database/AbstractCursor;->mRowIdColumnIndex:I @@ -591,6 +668,7 @@ Landroid/database/sqlite/SQLiteDebug$PagerStats;->memoryUsed:I Landroid/database/sqlite/SQLiteDebug$PagerStats;->pageCacheOverflow:I Landroid/database/sqlite/SQLiteOpenHelper;->mName:Ljava/lang/String; Landroid/ddm/DdmHandleAppName;->getAppName()Ljava/lang/String; +Landroid/graphics/AvoidXfermode$Mode;->AVOID:Landroid/graphics/AvoidXfermode$Mode; Landroid/graphics/AvoidXfermode$Mode;->TARGET:Landroid/graphics/AvoidXfermode$Mode; Landroid/graphics/BaseCanvas;->mNativeCanvasWrapper:J Landroid/graphics/Bitmap$Config;->nativeInt:I @@ -612,10 +690,10 @@ Landroid/graphics/Camera;->native_instance:J Landroid/graphics/Canvas;-><init>(J)V Landroid/graphics/Canvas;->release()V Landroid/graphics/Canvas;->save(I)I -Landroid/graphics/Canvas;->saveLayer(Landroid/graphics/RectF;Landroid/graphics/Paint;I)I -Landroid/graphics/Canvas;->saveLayer(FFFFLandroid/graphics/Paint;I)I -Landroid/graphics/Canvas;->saveLayerAlpha(Landroid/graphics/RectF;II)I Landroid/graphics/Canvas;->saveLayerAlpha(FFFFII)I +Landroid/graphics/Canvas;->saveLayerAlpha(Landroid/graphics/RectF;II)I +Landroid/graphics/Canvas;->saveLayer(FFFFLandroid/graphics/Paint;I)I +Landroid/graphics/Canvas;->saveLayer(Landroid/graphics/RectF;Landroid/graphics/Paint;I)I Landroid/graphics/ColorMatrixColorFilter;->setColorMatrix(Landroid/graphics/ColorMatrix;)V Landroid/graphics/drawable/AnimatedImageDrawable;->onAnimationEnd()V Landroid/graphics/drawable/AnimatedStateListDrawable$AnimatedStateListState;->mStateIds:Landroid/util/SparseIntArray; @@ -628,6 +706,7 @@ Landroid/graphics/drawable/BitmapDrawable;->getTint()Landroid/content/res/ColorS Landroid/graphics/drawable/BitmapDrawable;->getTintMode()Landroid/graphics/PorterDuff$Mode; Landroid/graphics/drawable/BitmapDrawable;->mTargetDensity:I Landroid/graphics/drawable/BitmapDrawable;->setBitmap(Landroid/graphics/Bitmap;)V +Landroid/graphics/drawable/ColorDrawable$ColorState;->mUseColor:I Landroid/graphics/drawable/DrawableContainer$DrawableContainerState;->mConstantPadding:Landroid/graphics/Rect; Landroid/graphics/drawable/DrawableContainer;->getOpticalInsets()Landroid/graphics/Insets; Landroid/graphics/drawable/DrawableContainer;->mDrawableContainerState:Landroid/graphics/drawable/DrawableContainer$DrawableContainerState; @@ -771,6 +850,7 @@ Landroid/hardware/Camera;->openLegacy(II)Landroid/hardware/Camera; Landroid/hardware/Camera;->postEventFromNative(Ljava/lang/Object;IIILjava/lang/Object;)V Landroid/hardware/display/WifiDisplayStatus;->mActiveDisplay:Landroid/hardware/display/WifiDisplay; Landroid/hardware/display/WifiDisplayStatus;->mDisplays:[Landroid/hardware/display/WifiDisplay; +Landroid/hardware/fingerprint/IFingerprintService$Stub$Proxy;-><init>(Landroid/os/IBinder;)V Landroid/hardware/HardwareBuffer;-><init>(J)V Landroid/hardware/HardwareBuffer;->mNativeObject:J Landroid/hardware/input/IInputManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/hardware/input/IInputManager; @@ -849,6 +929,9 @@ Landroid/icu/text/Transliterator;->transliterate(Ljava/lang/String;)Ljava/lang/S Landroid/icu/text/UFormat;->getLocale(Landroid/icu/util/ULocale$Type;)Landroid/icu/util/ULocale; Landroid/icu/util/Calendar;->getLocale(Landroid/icu/util/ULocale$Type;)Landroid/icu/util/ULocale; Landroid/inputmethodservice/InputMethodService;->mExtractEditText:Landroid/inputmethodservice/ExtractEditText; +Landroid/inputmethodservice/InputMethodService;->mRootView:Landroid/view/View; +Landroid/inputmethodservice/InputMethodService;->mSettingsObserver:Landroid/inputmethodservice/InputMethodService$SettingsObserver; +Landroid/inputmethodservice/InputMethodService$SettingsObserver;->shouldShowImeWithHardKeyboard()Z Landroid/location/CountryDetector;->detectCountry()Landroid/location/Country; Landroid/location/Country;->getCountryIso()Ljava/lang/String; Landroid/location/Country;->getSource()I @@ -945,11 +1028,14 @@ Landroid/media/AudioTrack;->mNativeTrackInJavaObj:J Landroid/media/AudioTrack;->mStreamType:I Landroid/media/AudioTrack;->native_release()V Landroid/media/AudioTrack;->postEventFromNative(Ljava/lang/Object;IIILjava/lang/Object;)V +Landroid/media/ExifInterface;->getDateTime()J Landroid/media/IAudioService;->getStreamMaxVolume(I)I Landroid/media/IAudioService;->getStreamVolume(I)I Landroid/media/IAudioService;->setStreamVolume(IIILjava/lang/String;)V Landroid/media/IAudioService$Stub;->asInterface(Landroid/os/IBinder;)Landroid/media/IAudioService; Landroid/media/IAudioService$Stub$Proxy;-><init>(Landroid/os/IBinder;)V +Landroid/media/IMediaScannerService;->scanFile(Ljava/lang/String;Ljava/lang/String;)V +Landroid/media/IMediaScannerService$Stub;->asInterface(Landroid/os/IBinder;)Landroid/media/IMediaScannerService; Landroid/media/IRemoteDisplayCallback;->onStateChanged(Landroid/media/RemoteDisplayState;)V Landroid/media/IVolumeController$Stub;->asInterface(Landroid/os/IBinder;)Landroid/media/IVolumeController; Landroid/media/JetPlayer;->mNativePlayerInJavaObj:J @@ -1060,11 +1146,16 @@ Landroid/net/ConnectivityManager;->TYPE_NONE:I Landroid/net/ConnectivityManager;->TYPE_PROXY:I Landroid/net/ConnectivityManager;->TYPE_WIFI_P2P:I Landroid/net/IConnectivityManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/net/IConnectivityManager; +Landroid/net/IConnectivityManager$Stub$Proxy;->getActiveLinkProperties()Landroid/net/LinkProperties; +Landroid/net/IConnectivityManager$Stub$Proxy;->getActiveNetworkInfo()Landroid/net/NetworkInfo; +Landroid/net/IConnectivityManager$Stub$Proxy;->getAllNetworkInfo()[Landroid/net/NetworkInfo; Landroid/net/IConnectivityManager$Stub$Proxy;->getAllNetworks()[Landroid/net/Network; Landroid/net/IConnectivityManager$Stub$Proxy;->getTetherableIfaces()[Ljava/lang/String; Landroid/net/IConnectivityManager$Stub$Proxy;->getTetherableUsbRegexs()[Ljava/lang/String; +Landroid/net/IConnectivityManager$Stub$Proxy;->getTetheredIfaces()[Ljava/lang/String; Landroid/net/IConnectivityManager$Stub$Proxy;-><init>(Landroid/os/IBinder;)V Landroid/net/INetworkStatsService$Stub$Proxy;->getMobileIfaces()[Ljava/lang/String; +Landroid/net/INetworkStatsService$Stub$Proxy;-><init>(Landroid/os/IBinder;)V Landroid/net/IpConfiguration;->httpProxy:Landroid/net/ProxyInfo; Landroid/net/LinkProperties;->setHttpProxy(Landroid/net/ProxyInfo;)V Landroid/net/LocalSocketImpl;->inboundFileDescriptors:[Ljava/io/FileDescriptor; @@ -1088,6 +1179,7 @@ Landroid/net/NetworkStats;->txBytes:[J Landroid/net/NetworkStats;->txPackets:[J Landroid/net/NetworkStats;->uid:[I Landroid/net/NetworkTemplate;->buildTemplateWifi()Landroid/net/NetworkTemplate; +Landroid/net/Proxy;->getProxy(Landroid/content/Context;Ljava/lang/String;)Ljava/net/Proxy; Landroid/net/ProxyInfo;-><init>(Ljava/lang/String;ILjava/lang/String;)V Landroid/net/SntpClient;-><init>()V Landroid/net/SSLCertificateSocketFactory;->castToOpenSSLSocket(Ljava/net/Socket;)Lcom/android/org/conscrypt/OpenSSLSocketImpl; @@ -1114,6 +1206,7 @@ Landroid/net/SSLCertificateSocketFactory;->setSoWriteTimeout(Ljava/net/Socket;I) Landroid/net/SSLCertificateSocketFactory;->TAG:Ljava/lang/String; Landroid/net/SSLCertificateSocketFactory;->verifyHostname(Ljava/net/Socket;Ljava/lang/String;)V Landroid/net/SSLSessionCache;->mSessionCache:Lcom/android/org/conscrypt/SSLClientSessionCache; +Landroid/net/TrafficStats;->getMobileIfaces()[Ljava/lang/String; Landroid/net/TrafficStats;->getRxBytes(Ljava/lang/String;)J Landroid/net/TrafficStats;->getStatsService()Landroid/net/INetworkStatsService; Landroid/net/TrafficStats;->getTxBytes(Ljava/lang/String;)J @@ -1122,12 +1215,12 @@ Landroid/net/wifi/IWifiManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/ Landroid/net/wifi/IWifiManager$Stub$Proxy;-><init>(Landroid/os/IBinder;)V Landroid/net/wifi/p2p/WifiP2pGroup;->getNetworkId()I Landroid/net/wifi/p2p/WifiP2pGroupList;->getGroupList()Ljava/util/Collection; +Landroid/net/wifi/p2p/WifiP2pManager$Channel;->mAsyncChannel:Lcom/android/internal/util/AsyncChannel; +Landroid/net/wifi/p2p/WifiP2pManager$Channel;->putListener(Ljava/lang/Object;)I Landroid/net/wifi/p2p/WifiP2pManager;->deletePersistentGroup(Landroid/net/wifi/p2p/WifiP2pManager$Channel;ILandroid/net/wifi/p2p/WifiP2pManager$ActionListener;)V Landroid/net/wifi/p2p/WifiP2pManager;->requestPersistentGroupInfo(Landroid/net/wifi/p2p/WifiP2pManager$Channel;Landroid/net/wifi/p2p/WifiP2pManager$PersistentGroupInfoListener;)V Landroid/net/wifi/p2p/WifiP2pManager;->setDeviceName(Landroid/net/wifi/p2p/WifiP2pManager$Channel;Ljava/lang/String;Landroid/net/wifi/p2p/WifiP2pManager$ActionListener;)V Landroid/net/wifi/p2p/WifiP2pManager;->setWifiP2pChannels(Landroid/net/wifi/p2p/WifiP2pManager$Channel;IILandroid/net/wifi/p2p/WifiP2pManager$ActionListener;)V -Landroid/net/wifi/p2p/WifiP2pManager$Channel;->mAsyncChannel:Lcom/android/internal/util/AsyncChannel; -Landroid/net/wifi/p2p/WifiP2pManager$Channel;->putListener(Ljava/lang/Object;)I Landroid/net/wifi/ScanResult;->anqpDomainId:I Landroid/net/wifi/ScanResult;->anqpLines:Ljava/util/List; Landroid/net/wifi/ScanResult;->distanceCm:I @@ -1157,10 +1250,12 @@ Landroid/net/wifi/ScanResult;->wifiSsid:Landroid/net/wifi/WifiSsid; Landroid/net/wifi/WifiConfiguration;->apBand:I Landroid/net/wifi/WifiConfiguration;->apChannel:I Landroid/net/wifi/WifiConfiguration;->defaultGwMacAddress:Ljava/lang/String; +Landroid/net/wifi/WifiConfiguration;->lastConnectUid:I Landroid/net/wifi/WifiConfiguration;->mIpConfiguration:Landroid/net/IpConfiguration; Landroid/net/wifi/WifiConfiguration;->validatedInternetAccess:Z Landroid/net/wifi/WifiEnterpriseConfig;->getCaCertificateAlias()Ljava/lang/String; Landroid/net/wifi/WifiEnterpriseConfig;->getClientCertificateAlias()Ljava/lang/String; +Landroid/net/wifi/WifiInfo;->DEFAULT_MAC_ADDRESS:Ljava/lang/String; Landroid/net/wifi/WifiInfo;->getMeteredHint()Z Landroid/net/wifi/WifiInfo;->mMacAddress:Ljava/lang/String; Landroid/net/wifi/WifiInfo;->removeDoubleQuotes(Ljava/lang/String;)Ljava/lang/String; @@ -1182,17 +1277,30 @@ Landroid/os/AsyncTask;->mTaskInvoked:Ljava/util/concurrent/atomic/AtomicBoolean; Landroid/os/AsyncTask;->mWorker:Landroid/os/AsyncTask$WorkerRunnable; Landroid/os/AsyncTask;->sDefaultExecutor:Ljava/util/concurrent/Executor; Landroid/os/AsyncTask;->setDefaultExecutor(Ljava/util/concurrent/Executor;)V +Landroid/os/BatteryStats$Counter;->getCountLocked(I)I Landroid/os/BatteryStats;->getUidStats()Landroid/util/SparseArray; Landroid/os/BatteryStats$HistoryItem;->states2:I Landroid/os/BatteryStats;->NUM_DATA_CONNECTION_TYPES:I Landroid/os/BatteryStats;->startIteratingHistoryLocked()Z Landroid/os/BatteryStats$Timer;->getTotalTimeLocked(JI)J +Landroid/os/BatteryStats$Uid;->getAudioTurnedOnTimer()Landroid/os/BatteryStats$Timer; Landroid/os/BatteryStats$Uid;->getFullWifiLockTime(JI)J +Landroid/os/BatteryStats$Uid;->getPackageStats()Landroid/util/ArrayMap; Landroid/os/BatteryStats$Uid;->getProcessStats()Landroid/util/ArrayMap; Landroid/os/BatteryStats$Uid;->getSensorStats()Landroid/util/SparseArray; Landroid/os/BatteryStats$Uid;->getUid()I +Landroid/os/BatteryStats$Uid;->getVideoTurnedOnTimer()Landroid/os/BatteryStats$Timer; Landroid/os/BatteryStats$Uid;->getWifiMulticastTime(JI)J Landroid/os/BatteryStats$Uid;->getWifiScanTime(JI)J +Landroid/os/BatteryStats$Uid$Pkg;->getServiceStats()Landroid/util/ArrayMap; +Landroid/os/BatteryStats$Uid$Pkg;->getWakeupAlarmStats()Landroid/util/ArrayMap; +Landroid/os/BatteryStats$Uid$Pkg$Serv;->getLaunches(I)I +Landroid/os/BatteryStats$Uid$Pkg$Serv;->getStartTime(JI)J +Landroid/os/BatteryStats$Uid$Proc;->countExcessivePowers()I +Landroid/os/BatteryStats$Uid$Proc$ExcessivePower;->overTime:J +Landroid/os/BatteryStats$Uid$Proc$ExcessivePower;->type:I +Landroid/os/BatteryStats$Uid$Proc$ExcessivePower;->usedTime:J +Landroid/os/BatteryStats$Uid$Proc;->getExcessivePower(I)Landroid/os/BatteryStats$Uid$Proc$ExcessivePower; Landroid/os/BatteryStats$Uid$Proc;->getForegroundTime(I)J Landroid/os/BatteryStats$Uid$Proc;->getSystemTime(I)J Landroid/os/BatteryStats$Uid$Proc;->getUserTime(I)J @@ -1239,6 +1347,7 @@ Landroid/os/Debug$MemoryInfo;->otherSwappedOut:I Landroid/os/Debug$MemoryInfo;->otherSwappedOutPss:I Landroid/os/Environment;->buildExternalStorageAppDataDirs(Ljava/lang/String;)[Ljava/io/File; Landroid/os/Environment;->getVendorDirectory()Ljava/io/File; +Landroid/os/Environment;->maybeTranslateEmulatedPathToInternal(Ljava/io/File;)Ljava/io/File; Landroid/os/FileObserver$ObserverThread;->onEvent(IILjava/lang/String;)V Landroid/os/FileUtils;->checksumCrc32(Ljava/io/File;)J Landroid/os/FileUtils;->copyFile(Ljava/io/File;Ljava/io/File;)Z @@ -1250,6 +1359,7 @@ Landroid/os/FileUtils;->setPermissions(Ljava/io/File;III)I Landroid/os/FileUtils;->setPermissions(Ljava/lang/String;III)I Landroid/os/FileUtils;->stringToFile(Ljava/io/File;Ljava/lang/String;)V Landroid/os/FileUtils;->stringToFile(Ljava/lang/String;Ljava/lang/String;)V +Landroid/os/FileUtils;->sync(Ljava/io/FileOutputStream;)Z Landroid/os/Handler;->getIMessenger()Landroid/os/IMessenger; Landroid/os/Handler;->hasCallbacks(Ljava/lang/Runnable;)Z Landroid/os/Handler;-><init>(Z)V @@ -1264,6 +1374,7 @@ Landroid/os/IPowerManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/os/IP Landroid/os/IPowerManager$Stub$Proxy;-><init>(Landroid/os/IBinder;)V Landroid/os/IPowerManager$Stub$Proxy;->isLightDeviceIdleMode()Z Landroid/os/IPowerManager;->userActivity(JII)V +Landroid/os/IServiceManager;->checkService(Ljava/lang/String;)Landroid/os/IBinder; Landroid/os/IServiceManager;->getService(Ljava/lang/String;)Landroid/os/IBinder; Landroid/os/IUserManager$Stub$Proxy;-><init>(Landroid/os/IBinder;)V Landroid/os/Looper;->mQueue:Landroid/os/MessageQueue; @@ -1307,6 +1418,7 @@ Landroid/os/Process;->getUidForPid(I)I Landroid/os/Process;->isIsolated(I)Z Landroid/os/Process;->readProcFile(Ljava/lang/String;[I[Ljava/lang/String;[J[F)Z Landroid/os/Process;->readProcLines(Ljava/lang/String;[Ljava/lang/String;[J)V +Landroid/os/Process;->setArgV0(Ljava/lang/String;)V Landroid/os/SELinux;->isSELinuxEnabled()Z Landroid/os/SELinux;->isSELinuxEnforced()Z Landroid/os/ServiceManager;->addService(Ljava/lang/String;Landroid/os/IBinder;)V @@ -1468,13 +1580,16 @@ Landroid/preference/PreferenceScreen;->mRootAdapter:Landroid/widget/ListAdapter; Landroid/print/PrinterId;->getServiceName()Landroid/content/ComponentName; Landroid/print/PrintJobInfo;->getAdvancedOptions()Landroid/os/Bundle; Landroid/print/PrintJobInfo;->getDocumentInfo()Landroid/print/PrintDocumentInfo; +Landroid/provider/Browser$BookmarkColumns;->DATE:Ljava/lang/String; Landroid/provider/Browser;->BOOKMARKS_URI:Landroid/net/Uri; Landroid/provider/Browser;->canClearHistory(Landroid/content/ContentResolver;)Z Landroid/provider/Browser;->clearHistory(Landroid/content/ContentResolver;)V Landroid/provider/Browser;->clearSearches(Landroid/content/ContentResolver;)V Landroid/provider/Browser;->deleteFromHistory(Landroid/content/ContentResolver;Ljava/lang/String;)V Landroid/provider/Browser;->getVisitedHistory(Landroid/content/ContentResolver;)[Ljava/lang/String; +Landroid/provider/Browser;->SEARCHES_URI:Landroid/net/Uri; Landroid/provider/Browser;->sendString(Landroid/content/Context;Ljava/lang/String;Ljava/lang/String;)V +Landroid/provider/Browser;->updateVisitedHistory(Landroid/content/ContentResolver;Ljava/lang/String;Z)V Landroid/provider/CalendarContract$CalendarAlerts;->findNextAlarmTime(Landroid/content/ContentResolver;J)J Landroid/provider/CalendarContract$CalendarAlerts;->rescheduleMissedAlarms(Landroid/content/ContentResolver;Landroid/content/Context;Landroid/app/AlarmManager;)V Landroid/provider/Settings$ContentProviderHolder;->mContentProvider:Landroid/content/IContentProvider; @@ -1702,6 +1817,7 @@ Landroid/R$styleable;->View_visibility:I Landroid/R$styleable;->Window:[I Landroid/R$styleable;->Window_windowBackground:I Landroid/R$styleable;->Window_windowFrame:I +Landroid/security/keystore/AndroidKeyStoreProvider;->getKeyStoreOperationHandle(Ljava/lang/Object;)J Landroid/security/KeyStore;->getInstance()Landroid/security/KeyStore; Landroid/security/keystore/KeychainProtectionParams;->clearSecret()V Landroid/security/keystore/KeychainProtectionParams;->getKeyDerivationParams()Landroid/security/keystore/KeyDerivationParams; @@ -1850,6 +1966,7 @@ Landroid/telephony/SmsMessage;->mWrappedSmsMessage:Lcom/android/internal/telepho Landroid/telephony/SubscriptionManager;->getActiveSubscriptionIdList()[I Landroid/telephony/SubscriptionManager;->getAllSubscriptionInfoCount()I Landroid/telephony/SubscriptionManager;->getAllSubscriptionInfoList()Ljava/util/List; +Landroid/telephony/SubscriptionManager;->getDefaultDataPhoneId()I Landroid/telephony/SubscriptionManager;->getDefaultDataSubscriptionInfo()Landroid/telephony/SubscriptionInfo; Landroid/telephony/SubscriptionManager;->getDefaultSmsPhoneId()I Landroid/telephony/SubscriptionManager;->getDefaultVoiceSubscriptionInfo()Landroid/telephony/SubscriptionInfo; @@ -1889,6 +2006,7 @@ Landroid/telephony/TelephonyManager;-><init>()V Landroid/telephony/TelephonyManager;->isMultiSimEnabled()Z Landroid/telephony/TelephonyManager;->isNetworkRoaming(I)Z Landroid/telephony/TelephonyManager;->isVolteAvailable()Z +Landroid/telephony/TelephonyManager;->mSubscriptionManager:Landroid/telephony/SubscriptionManager; Landroid/text/AndroidBidi;->bidi(I[C[B)I Landroid/text/DynamicLayout;-><init>(Ljava/lang/CharSequence;Ljava/lang/CharSequence;Landroid/text/TextPaint;ILandroid/text/Layout$Alignment;Landroid/text/TextDirectionHeuristic;FFZIIILandroid/text/TextUtils$TruncateAt;I)V Landroid/text/DynamicLayout;->sStaticLayout:Landroid/text/StaticLayout; @@ -1945,6 +2063,7 @@ Landroid/text/StaticLayout$LineBreaks;->widths:[F Landroid/text/StaticLayout;->mColumns:I Landroid/text/StaticLayout;->mLineCount:I Landroid/text/StaticLayout;->mLines:[I +Landroid/text/StaticLayout;->mMaximumVisibleLineCount:I Landroid/text/TextLine;->mCharacterStyleSpanSet:Landroid/text/SpanSet; Landroid/text/TextLine;->mMetricAffectingSpanSpanSet:Landroid/text/SpanSet; Landroid/text/TextLine;->mReplacementSpanSpanSet:Landroid/text/SpanSet; @@ -1976,6 +2095,8 @@ Landroid/util/Pools$SynchronizedPool;->acquire()Ljava/lang/Object; Landroid/util/Pools$SynchronizedPool;-><init>(I)V Landroid/util/Rational;->mDenominator:I Landroid/util/Rational;->mNumerator:I +Landroid/util/Singleton;->get()Ljava/lang/Object; +Landroid/util/Singleton;-><init>()V Landroid/util/Singleton;->mInstance:Ljava/lang/Object; Landroid/util/Slog;->d(Ljava/lang/String;Ljava/lang/String;)I Landroid/util/Slog;->w(Ljava/lang/String;Ljava/lang/String;)I @@ -1984,6 +2105,7 @@ Landroid/util/SparseIntArray;->mSize:I Landroid/util/SparseIntArray;->mValues:[I Landroid/view/accessibility/AccessibilityManager;->getInstance(Landroid/content/Context;)Landroid/view/accessibility/AccessibilityManager; Landroid/view/accessibility/AccessibilityManager;->isHighTextContrastEnabled()Z +Landroid/view/accessibility/AccessibilityManager;->mAccessibilityStateChangeListeners:Landroid/util/ArrayMap; Landroid/view/accessibility/AccessibilityManager;->mIsEnabled:Z Landroid/view/accessibility/AccessibilityManager;->mIsHighTextContrastEnabled:Z Landroid/view/accessibility/AccessibilityManager;->sInstance:Landroid/view/accessibility/AccessibilityManager; @@ -2033,6 +2155,7 @@ Landroid/view/InputEventSender;->dispatchInputEventFinished(IZ)V Landroid/view/inputmethod/InputMethodInfo;->mSubtypes:Landroid/view/inputmethod/InputMethodSubtypeArray; Landroid/view/inputmethod/InputMethodManager;->finishInputLocked()V Landroid/view/inputmethod/InputMethodManager;->focusIn(Landroid/view/View;)V +Landroid/view/inputmethod/InputMethodManager;->focusOut(Landroid/view/View;)V Landroid/view/inputmethod/InputMethodManager;->getInputMethodWindowVisibleHeight()I Landroid/view/inputmethod/InputMethodManager;->mCurId:Ljava/lang/String; Landroid/view/inputmethod/InputMethodManager;->mCurRootView:Landroid/view/View; @@ -2041,6 +2164,7 @@ Landroid/view/inputmethod/InputMethodManager;->mNextServedView:Landroid/view/Vie Landroid/view/inputmethod/InputMethodManager;->mServedView:Landroid/view/View; Landroid/view/inputmethod/InputMethodManager;->mService:Lcom/android/internal/view/IInputMethodManager; Landroid/view/inputmethod/InputMethodManager;->notifyUserAction()V +Landroid/view/inputmethod/InputMethodManager;->peekInstance()Landroid/view/inputmethod/InputMethodManager; Landroid/view/inputmethod/InputMethodManager;->showSoftInputUnchecked(ILandroid/os/ResultReceiver;)V Landroid/view/inputmethod/InputMethodManager;->sInstance:Landroid/view/inputmethod/InputMethodManager; Landroid/view/inputmethod/InputMethodManager;->windowDismissed(Landroid/os/IBinder;)V @@ -2054,6 +2178,7 @@ Landroid/view/IWindowManager;->setShelfHeight(ZI)V Landroid/view/IWindowManager;->setStrictModeVisualIndicatorPreference(Ljava/lang/String;)V Landroid/view/IWindowManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/view/IWindowManager; Landroid/view/IWindowManager$Stub$Proxy;->getBaseDisplayDensity(I)I +Landroid/view/IWindowManager$Stub$Proxy;->getDockedStackSide()I Landroid/view/IWindowManager$Stub$Proxy;->getInitialDisplayDensity(I)I Landroid/view/IWindowManager$Stub$Proxy;->hasNavigationBar()Z Landroid/view/IWindowManager$Stub$Proxy;-><init>(Landroid/os/IBinder;)V @@ -2084,6 +2209,7 @@ Landroid/view/LayoutInflater;->mFactorySet:Z Landroid/view/LayoutInflater;->mPrivateFactory:Landroid/view/LayoutInflater$Factory2; Landroid/view/LayoutInflater;->sConstructorMap:Ljava/util/HashMap; Landroid/view/LayoutInflater;->setPrivateFactory(Landroid/view/LayoutInflater$Factory2;)V +Landroid/view/MotionEvent;->getPointerIdBits()I Landroid/view/MotionEvent;->HISTORY_CURRENT:I Landroid/view/MotionEvent;->mNativePtr:J Landroid/view/MotionEvent;->nativeGetRawAxisValue(JIII)F @@ -2091,6 +2217,7 @@ Landroid/view/MotionEvent;->obtain()Landroid/view/MotionEvent; Landroid/view/MotionEvent$PointerCoords;->mPackedAxisBits:J Landroid/view/MotionEvent$PointerCoords;->mPackedAxisValues:[F Landroid/view/MotionEvent;->scale(F)V +Landroid/view/MotionEvent;->split(I)Landroid/view/MotionEvent; Landroid/view/PointerIcon;->load(Landroid/content/Context;)Landroid/view/PointerIcon; Landroid/view/PointerIcon;->mBitmapFrames:[Landroid/graphics/Bitmap; Landroid/view/PointerIcon;->mBitmap:Landroid/graphics/Bitmap; @@ -2111,6 +2238,8 @@ Landroid/view/RemoteAnimationTarget;->sourceContainerBounds:Landroid/graphics/Re Landroid/view/RemoteAnimationTarget;->taskId:I Landroid/view/RemoteAnimationTarget;->windowConfiguration:Landroid/app/WindowConfiguration; Landroid/view/RenderNodeAnimator;->callOnFinished(Landroid/view/RenderNodeAnimator;)V +Landroid/view/RenderNode;->discardDisplayList()V +Landroid/view/RenderNode;->output()V Landroid/view/ScaleGestureDetector;->mListener:Landroid/view/ScaleGestureDetector$OnScaleGestureListener; Landroid/view/ScaleGestureDetector;->mMinSpan:I Landroid/view/SurfaceControl$PhysicalDisplayInfo;->appVsyncOffsetNanos:J @@ -2145,6 +2274,7 @@ Landroid/view/textclassifier/logging/SmartSelectionEventTracker$SelectionEvent;- Landroid/view/textclassifier/TextClassificationManager;->getTextClassifier(I)Landroid/view/textclassifier/TextClassifier; Landroid/view/textservice/TextServicesManager;->isSpellCheckerEnabled()Z Landroid/view/TextureView;->destroyHardwareLayer()V +Landroid/view/TextureView;->destroyHardwareResources()V Landroid/view/TextureView;->mLayer:Landroid/view/TextureLayer; Landroid/view/TextureView;->mNativeWindow:J Landroid/view/TextureView;->mSurface:Landroid/graphics/SurfaceTexture; @@ -2156,6 +2286,7 @@ Landroid/view/VelocityTracker$Estimator;->xCoeff:[F Landroid/view/VelocityTracker$Estimator;->yCoeff:[F Landroid/view/VelocityTracker;->obtain(Ljava/lang/String;)Landroid/view/VelocityTracker; Landroid/view/View;->applyDrawableToTransparentRegion(Landroid/graphics/drawable/Drawable;Landroid/graphics/Region;)V +Landroid/view/View$AttachInfo;->mContentInsets:Landroid/graphics/Rect; Landroid/view/View$AttachInfo;->mDrawingTime:J Landroid/view/View$AttachInfo;->mStableInsets:Landroid/graphics/Rect; Landroid/view/View;->clearAccessibilityFocus()V @@ -2174,12 +2305,14 @@ Landroid/view/View;->dispatchDetachedFromWindow()V Landroid/view/View;->fitsSystemWindows()Z Landroid/view/View;->getAccessibilityDelegate()Landroid/view/View$AccessibilityDelegate; Landroid/view/View;->getBoundsOnScreen(Landroid/graphics/Rect;)V +Landroid/view/View;->getHorizontalScrollFactor()F Landroid/view/View;->getInverseMatrix()Landroid/graphics/Matrix; Landroid/view/View;->getListenerInfo()Landroid/view/View$ListenerInfo; Landroid/view/View;->getLocationOnScreen()[I Landroid/view/View;->getRawTextAlignment()I Landroid/view/View;->getRawTextDirection()I Landroid/view/View;->getTransitionAlpha()F +Landroid/view/View;->getVerticalScrollFactor()F Landroid/view/View;->getViewRootImpl()Landroid/view/ViewRootImpl; Landroid/view/View;->getWindowDisplayFrame(Landroid/graphics/Rect;)V Landroid/view/ViewGroup;->dispatchViewAdded(Landroid/view/View;)V @@ -2194,18 +2327,23 @@ Landroid/view/ViewGroup;->mChildren:[Landroid/view/View; Landroid/view/ViewGroup;->mFirstTouchTarget:Landroid/view/ViewGroup$TouchTarget; Landroid/view/ViewGroup;->mGroupFlags:I Landroid/view/ViewGroup;->mOnHierarchyChangeListener:Landroid/view/ViewGroup$OnHierarchyChangeListener; +Landroid/view/ViewGroup;->mPersistentDrawingCache:I +Landroid/view/ViewGroup;->offsetChildrenTopAndBottom(I)V Landroid/view/ViewGroup;->resetResolvedDrawables()V Landroid/view/ViewGroup;->resetResolvedLayoutDirection()V Landroid/view/ViewGroup;->resetResolvedPadding()V Landroid/view/ViewGroup;->resetResolvedTextAlignment()V Landroid/view/ViewGroup;->resetResolvedTextDirection()V Landroid/view/ViewGroup;->suppressLayout(Z)V +Landroid/view/View;->includeForAccessibility()Z Landroid/view/View;->initializeScrollbars(Landroid/content/res/TypedArray;)V Landroid/view/View;->internalSetPadding(IIII)V Landroid/view/View;->isPaddingResolved()Z Landroid/view/View;->isVisibleToUser(Landroid/graphics/Rect;)Z Landroid/view/View;->isVisibleToUser()Z +Landroid/view/View$ListenerInfo;-><init>()V Landroid/view/View$ListenerInfo;->mOnClickListener:Landroid/view/View$OnClickListener; +Landroid/view/View$ListenerInfo;->mOnFocusChangeListener:Landroid/view/View$OnFocusChangeListener; Landroid/view/View$ListenerInfo;->mOnLongClickListener:Landroid/view/View$OnLongClickListener; Landroid/view/View$ListenerInfo;->mOnTouchListener:Landroid/view/View$OnTouchListener; Landroid/view/View;->mAccessibilityDelegate:Landroid/view/View$AccessibilityDelegate; @@ -2218,10 +2356,13 @@ Landroid/view/View;->mLeft:I Landroid/view/View;->mListenerInfo:Landroid/view/View$ListenerInfo; Landroid/view/View;->mMinHeight:I Landroid/view/View;->mMinWidth:I +Landroid/view/View;->mPaddingBottom:I Landroid/view/View;->mPaddingLeft:I Landroid/view/View;->mPaddingRight:I +Landroid/view/View;->mPaddingTop:I Landroid/view/View;->mParent:Landroid/view/ViewParent; Landroid/view/View;->mPrivateFlags3:I +Landroid/view/View;->mPrivateFlags:I Landroid/view/View;->mRecreateDisplayList:Z Landroid/view/View;->mResources:Landroid/content/res/Resources; Landroid/view/View;->mRight:I @@ -2234,6 +2375,8 @@ Landroid/view/View;->mTop:I Landroid/view/View;->mUnscaledDrawingCache:Landroid/graphics/Bitmap; Landroid/view/View;->mViewFlags:I Landroid/view/View;->notifySubtreeAccessibilityStateChangedIfNeeded()V +Landroid/view/View;->onDrawVerticalScrollBar(Landroid/graphics/Canvas;Landroid/graphics/drawable/Drawable;IIII)V +Landroid/view/View;->performAccessibilityActionInternal(ILandroid/os/Bundle;)Z Landroid/view/View;->recomputePadding()V Landroid/view/View;->requestAccessibilityFocus()Z Landroid/view/View;->resetDisplayList()V @@ -2294,7 +2437,10 @@ Landroid/view/WindowManager$LayoutParams;->NEEDS_MENU_SET_TRUE:I Landroid/view/WindowManager$LayoutParams;->userActivityTimeout:J Landroid/view/Window;->mAppName:Ljava/lang/String; Landroid/view/Window;->mAppToken:Landroid/os/IBinder; +Landroid/view/Window;->mCallback:Landroid/view/Window$Callback; +Landroid/view/Window;->mContext:Landroid/content/Context; Landroid/view/Window;->mHardwareAccelerated:Z +Landroid/view/Window;->mWindowStyle:Landroid/content/res/TypedArray; Landroid/webkit/IWebViewUpdateService$Stub$Proxy;-><init>(Landroid/os/IBinder;)V Landroid/webkit/WebResourceResponse;->mImmutable:Z Landroid/webkit/WebSyncManager;->mHandler:Landroid/os/Handler; @@ -2307,6 +2453,7 @@ Landroid/webkit/WebViewFactory;->getProvider()Landroid/webkit/WebViewFactoryProv Landroid/webkit/WebViewFactory;->getWebViewContextAndSetProvider()Landroid/content/Context; Landroid/webkit/WebViewFactory;->sPackageInfo:Landroid/content/pm/PackageInfo; Landroid/webkit/WebViewFactory;->sProviderInstance:Landroid/webkit/WebViewFactoryProvider; +Landroid/webkit/WebView;->getTouchIconUrl()Ljava/lang/String; Landroid/webkit/WebView;->getVisibleTitleHeight()I Landroid/webkit/WebView;->isPaused()Z Landroid/webkit/WebView;->mProvider:Landroid/webkit/WebViewProvider; @@ -2328,6 +2475,8 @@ Landroid/widget/AbsListView;->mIsChildViewEnabled:Z Landroid/widget/AbsListView;->mMaximumVelocity:I Landroid/widget/AbsListView;->mMotionPosition:I Landroid/widget/AbsListView;->mOnScrollListener:Landroid/widget/AbsListView$OnScrollListener; +Landroid/widget/AbsListView;->mPendingCheckForLongPress:Landroid/widget/AbsListView$CheckForLongPress; +Landroid/widget/AbsListView;->mPendingCheckForTap:Landroid/widget/AbsListView$CheckForTap; Landroid/widget/AbsListView;->mRecycler:Landroid/widget/AbsListView$RecycleBin; Landroid/widget/AbsListView;->mSelectionTopPadding:I Landroid/widget/AbsListView;->mSelectorPosition:I @@ -2340,6 +2489,8 @@ Landroid/widget/AbsListView;->performLongPress(Landroid/view/View;IJ)Z Landroid/widget/AbsListView$RecycleBin;->clear()V Landroid/widget/AbsListView$RecycleBin;->mRecyclerListener:Landroid/widget/AbsListView$RecyclerListener; Landroid/widget/AbsListView;->reportScrollStateChange(I)V +Landroid/widget/AbsListView$SavedState;->firstId:J +Landroid/widget/AbsListView$SavedState;->viewTop:I Landroid/widget/AbsListView;->smoothScrollBy(IIZZ)V Landroid/widget/AbsListView;->trackMotionScroll(II)Z Landroid/widget/AbsSeekBar;->mIsDragging:Z @@ -2358,7 +2509,9 @@ Landroid/widget/AutoCompleteTextView;->doBeforeTextChanged()V Landroid/widget/AutoCompleteTextView;->ensureImeVisible(Z)V Landroid/widget/AutoCompleteTextView;->mPopup:Landroid/widget/ListPopupWindow; Landroid/widget/AutoCompleteTextView;->setDropDownAlwaysVisible(Z)V +Landroid/widget/AutoCompleteTextView;->setForceIgnoreOutsideTouch(Z)V Landroid/widget/CompoundButton;->mButtonDrawable:Landroid/graphics/drawable/Drawable; +Landroid/widget/CompoundButton;->mOnCheckedChangeListener:Landroid/widget/CompoundButton$OnCheckedChangeListener; Landroid/widget/CursorAdapter;->mChangeObserver:Landroid/widget/CursorAdapter$ChangeObserver; Landroid/widget/CursorAdapter;->mDataSetObserver:Landroid/database/DataSetObserver; Landroid/widget/CursorAdapter;->mDataValid:Z @@ -2369,6 +2522,7 @@ Landroid/widget/Editor;->invalidateTextDisplayList()V Landroid/widget/Editor;->mShowCursor:J Landroid/widget/Editor;->mShowSoftInputOnFocus:Z Landroid/widget/ExpandableListView;->mChildDivider:Landroid/graphics/drawable/Drawable; +Landroid/widget/ExpandableListView;->mGroupIndicator:Landroid/graphics/drawable/Drawable; Landroid/widget/FastScroller;->mContainerRect:Landroid/graphics/Rect; Landroid/widget/FastScroller;->mHeaderCount:I Landroid/widget/FastScroller;->mLongList:Z @@ -2475,13 +2629,17 @@ Landroid/widget/RemoteViews;->mBitmapCache:Landroid/widget/RemoteViews$BitmapCac Landroid/widget/RemoteViews;->mergeRemoteViews(Landroid/widget/RemoteViews;)V Landroid/widget/RemoteViews;->mPortrait:Landroid/widget/RemoteViews; Landroid/widget/RemoteViews$ReflectionAction;->methodName:Ljava/lang/String; +Landroid/widget/RemoteViews$ReflectionAction;->value:Ljava/lang/Object; +Landroid/widget/RemoteViews$SetOnClickPendingIntent;->pendingIntent:Landroid/app/PendingIntent; Landroid/widget/ScrollBarDrawable;->mVerticalThumb:Landroid/graphics/drawable/Drawable; Landroid/widget/ScrollBarDrawable;->setHorizontalThumbDrawable(Landroid/graphics/drawable/Drawable;)V Landroid/widget/ScrollBarDrawable;->setVerticalThumbDrawable(Landroid/graphics/drawable/Drawable;)V +Landroid/widget/Scroller;->mInterpolator:Landroid/view/animation/Interpolator; Landroid/widget/ScrollView;->mChildToScrollTo:Landroid/view/View; Landroid/widget/ScrollView;->mEdgeGlowBottom:Landroid/widget/EdgeEffect; Landroid/widget/ScrollView;->mEdgeGlowTop:Landroid/widget/EdgeEffect; Landroid/widget/ScrollView;->mIsBeingDragged:Z +Landroid/widget/ScrollView;->mMinimumVelocity:I Landroid/widget/ScrollView;->mOverflingDistance:I Landroid/widget/ScrollView;->mOverscrollDistance:I Landroid/widget/ScrollView;->mScroller:Landroid/widget/OverScroller; @@ -2495,7 +2653,11 @@ Landroid/widget/SlidingDrawer;->mTopOffset:I Landroid/widget/Spinner;->mPopup:Landroid/widget/Spinner$SpinnerPopup; Landroid/widget/Switch;->mThumbDrawable:Landroid/graphics/drawable/Drawable; Landroid/widget/Switch;->mTrackDrawable:Landroid/graphics/drawable/Drawable; +Landroid/widget/TabHost$IntentContentStrategy;->getContentView()Landroid/view/View; +Landroid/widget/TabHost$IntentContentStrategy;->tabClosed()V +Landroid/widget/TabHost;->mTabSpecs:Ljava/util/List; Landroid/widget/TabHost$TabSpec;->mContentStrategy:Landroid/widget/TabHost$ContentStrategy; +Landroid/widget/TabWidget;->mSelectedTab:I Landroid/widget/TabWidget;->setTabSelectionListener(Landroid/widget/TabWidget$OnTabSelectionChanged;)V Landroid/widget/TextView;->assumeLayout()V Landroid/widget/TextView;->createEditorIfNeeded()V @@ -2513,7 +2675,11 @@ Landroid/widget/TextView;->mSingleLine:Z Landroid/widget/TextView;->mTextPaint:Landroid/text/TextPaint; Landroid/widget/TextView;->setText(Ljava/lang/CharSequence;Landroid/widget/TextView$BufferType;ZI)V Landroid/widget/Toast;->getService()Landroid/app/INotificationManager; +Landroid/widget/Toast;->getWindowParams()Landroid/view/WindowManager$LayoutParams; +Landroid/widget/Toast;->mTN:Landroid/widget/Toast$TN; Landroid/widget/Toast;->sService:Landroid/app/INotificationManager; +Landroid/widget/Toast$TN;->mNextView:Landroid/view/View; +Landroid/widget/Toast$TN;->mParams:Landroid/view/WindowManager$LayoutParams; Landroid/widget/VideoView2;->getMediaController()Landroid/media/session/MediaController; Landroid/widget/VideoView2$OnViewTypeChangedListener;->onViewTypeChanged(Landroid/view/View;I)V Landroid/widget/VideoView2;->setOnViewTypeChangedListener(Landroid/widget/VideoView2$OnViewTypeChangedListener;)V @@ -2596,6 +2762,7 @@ Lcom/android/internal/app/AlertController$RecycleListView;-><init>(Landroid/cont Lcom/android/internal/app/IAppOpsService$Stub;->asInterface(Landroid/os/IBinder;)Lcom/android/internal/app/IAppOpsService; Lcom/android/internal/app/IAppOpsService$Stub$Proxy;->checkOperation(IILjava/lang/String;)I Lcom/android/internal/app/IAppOpsService$Stub$Proxy;-><init>(Landroid/os/IBinder;)V +Lcom/android/internal/app/IAppOpsService$Stub$Proxy;->setMode(IILjava/lang/String;I)V Lcom/android/internal/app/IBatteryStats;->getStatistics()[B Lcom/android/internal/app/IBatteryStats$Stub;->asInterface(Landroid/os/IBinder;)Lcom/android/internal/app/IBatteryStats; Lcom/android/internal/app/IBatteryStats$Stub$Proxy;-><init>(Landroid/os/IBinder;)V @@ -2623,10 +2790,12 @@ Lcom/android/internal/os/BatteryStatsHelper;->refreshStats(II)V Lcom/android/internal/os/BatteryStatsImpl;->computeBatteryRealtime(JI)J Lcom/android/internal/os/BatteryStatsImpl;->computeBatteryUptime(JI)J Lcom/android/internal/os/BatteryStatsImpl;->CREATOR:Landroid/os/Parcelable$Creator; +Lcom/android/internal/os/BatteryStatsImpl;->getBatteryRealtime(J)J Lcom/android/internal/os/BatteryStatsImpl;->getDischargeAmount(I)I Lcom/android/internal/os/BatteryStatsImpl;->getGlobalWifiRunningTime(JI)J Lcom/android/internal/os/BatteryStatsImpl;->getScreenOnTime(JI)J Lcom/android/internal/os/BatteryStatsImpl;->getUidStats()Landroid/util/SparseArray; +Lcom/android/internal/os/BatteryStatsImpl;->getUidStatsLocked(I)Lcom/android/internal/os/BatteryStatsImpl$Uid; Lcom/android/internal/os/BatteryStatsImpl$Timer;->getCountLocked(I)I Lcom/android/internal/os/BatteryStatsImpl$Timer;->getTotalTimeLocked(JI)J Lcom/android/internal/os/BatteryStatsImpl$Uid;->getProcessStats()Landroid/util/ArrayMap; @@ -2694,6 +2863,18 @@ Lcom/android/internal/R$string;->kilobyteShort:I Lcom/android/internal/R$string;->megabyteShort:I Lcom/android/internal/R$string;->petabyteShort:I Lcom/android/internal/R$string;->terabyteShort:I +Lcom/android/internal/R$styleable;->AbsListView_cacheColorHint:I +Lcom/android/internal/R$styleable;->AbsListView_choiceMode:I +Lcom/android/internal/R$styleable;->AbsListView_drawSelectorOnTop:I +Lcom/android/internal/R$styleable;->AbsListView_fastScrollAlwaysVisible:I +Lcom/android/internal/R$styleable;->AbsListView_fastScrollEnabled:I +Lcom/android/internal/R$styleable;->AbsListView:[I +Lcom/android/internal/R$styleable;->AbsListView_listSelector:I +Lcom/android/internal/R$styleable;->AbsListView_scrollingCache:I +Lcom/android/internal/R$styleable;->AbsListView_smoothScrollbar:I +Lcom/android/internal/R$styleable;->AbsListView_stackFromBottom:I +Lcom/android/internal/R$styleable;->AbsListView_textFilterEnabled:I +Lcom/android/internal/R$styleable;->AbsListView_transcriptMode:I Lcom/android/internal/R$styleable;->AccountAuthenticator_accountPreferences:I Lcom/android/internal/R$styleable;->AccountAuthenticator_accountType:I Lcom/android/internal/R$styleable;->AccountAuthenticator_customTokens:I @@ -2701,6 +2882,28 @@ Lcom/android/internal/R$styleable;->AccountAuthenticator:[I Lcom/android/internal/R$styleable;->AccountAuthenticator_icon:I Lcom/android/internal/R$styleable;->AccountAuthenticator_label:I Lcom/android/internal/R$styleable;->AccountAuthenticator_smallIcon:I +Lcom/android/internal/R$styleable;->AndroidManifestActivity_allowTaskReparenting:I +Lcom/android/internal/R$styleable;->AndroidManifestActivity_configChanges:I +Lcom/android/internal/R$styleable;->AndroidManifestActivity_description:I +Lcom/android/internal/R$styleable;->AndroidManifestActivity_enabled:I +Lcom/android/internal/R$styleable;->AndroidManifestActivity_excludeFromRecents:I +Lcom/android/internal/R$styleable;->AndroidManifestActivity_exported:I +Lcom/android/internal/R$styleable;->AndroidManifestActivity_hardwareAccelerated:I +Lcom/android/internal/R$styleable;->AndroidManifestActivity:[I +Lcom/android/internal/R$styleable;->AndroidManifestActivity_icon:I +Lcom/android/internal/R$styleable;->AndroidManifestActivity_immersive:I +Lcom/android/internal/R$styleable;->AndroidManifestActivity_label:I +Lcom/android/internal/R$styleable;->AndroidManifestActivity_launchMode:I +Lcom/android/internal/R$styleable;->AndroidManifestActivity_logo:I +Lcom/android/internal/R$styleable;->AndroidManifestActivity_name:I +Lcom/android/internal/R$styleable;->AndroidManifestActivity_noHistory:I +Lcom/android/internal/R$styleable;->AndroidManifestActivity_permission:I +Lcom/android/internal/R$styleable;->AndroidManifestActivity_process:I +Lcom/android/internal/R$styleable;->AndroidManifestActivity_screenOrientation:I +Lcom/android/internal/R$styleable;->AndroidManifestActivity_taskAffinity:I +Lcom/android/internal/R$styleable;->AndroidManifestActivity_theme:I +Lcom/android/internal/R$styleable;->AndroidManifestActivity_uiOptions:I +Lcom/android/internal/R$styleable;->AndroidManifestActivity_windowSoftInputMode:I Lcom/android/internal/R$styleable;->AndroidManifestApplication_enabled:I Lcom/android/internal/R$styleable;->AndroidManifestApplication_hardwareAccelerated:I Lcom/android/internal/R$styleable;->AndroidManifestApplication:[I @@ -2712,8 +2915,21 @@ Lcom/android/internal/R$styleable;->AndroidManifestApplication_process:I Lcom/android/internal/R$styleable;->AndroidManifestApplication_supportsRtl:I Lcom/android/internal/R$styleable;->AndroidManifestApplication_theme:I Lcom/android/internal/R$styleable;->AndroidManifestApplication_uiOptions:I +Lcom/android/internal/R$styleable;->AndroidManifestData:[I Lcom/android/internal/R$styleable;->AndroidManifest:[I Lcom/android/internal/R$styleable;->AndroidManifest_installLocation:I +Lcom/android/internal/R$styleable;->AndroidManifestIntentFilter:[I +Lcom/android/internal/R$styleable;->AndroidManifestIntentFilter_priority:I +Lcom/android/internal/R$styleable;->AndroidManifestMetaData:[I +Lcom/android/internal/R$styleable;->AndroidManifestMetaData_name:I +Lcom/android/internal/R$styleable;->AndroidManifestMetaData_resource:I +Lcom/android/internal/R$styleable;->AndroidManifestMetaData_value:I +Lcom/android/internal/R$styleable;->AndroidManifestService_enabled:I +Lcom/android/internal/R$styleable;->AndroidManifestService_exported:I +Lcom/android/internal/R$styleable;->AndroidManifestService:[I +Lcom/android/internal/R$styleable;->AndroidManifestService_name:I +Lcom/android/internal/R$styleable;->AndroidManifestService_permission:I +Lcom/android/internal/R$styleable;->AndroidManifestService_process:I Lcom/android/internal/R$styleable;->AndroidManifest_sharedUserId:I Lcom/android/internal/R$styleable;->AndroidManifestUsesPermission:[I Lcom/android/internal/R$styleable;->AndroidManifestUsesPermission_name:I @@ -2727,6 +2943,7 @@ Lcom/android/internal/R$styleable;->CheckBoxPreference:[I Lcom/android/internal/R$styleable;->CheckBoxPreference_summaryOff:I Lcom/android/internal/R$styleable;->CheckBoxPreference_summaryOn:I Lcom/android/internal/R$styleable;->CompoundButton_button:I +Lcom/android/internal/R$styleable;->CompoundButton_checked:I Lcom/android/internal/R$styleable;->CompoundButton:[I Lcom/android/internal/R$styleable;->DialogPreference_dialogTitle:I Lcom/android/internal/R$styleable;->DialogPreference:[I @@ -2737,6 +2954,15 @@ Lcom/android/internal/R$styleable;->ImageView:[I Lcom/android/internal/R$styleable;->ImageView_src:I Lcom/android/internal/R$styleable;->ListPreference_entries:I Lcom/android/internal/R$styleable;->ListPreference:[I +Lcom/android/internal/R$styleable;->ListView_dividerHeight:I +Lcom/android/internal/R$styleable;->ListView_divider:I +Lcom/android/internal/R$styleable;->ListView_entries:I +Lcom/android/internal/R$styleable;->ListView_footerDividersEnabled:I +Lcom/android/internal/R$styleable;->ListView_headerDividersEnabled:I +Lcom/android/internal/R$styleable;->ListView:[I +Lcom/android/internal/R$styleable;->ListView_overScrollFooter:I +Lcom/android/internal/R$styleable;->ListView_overScrollHeader:I +Lcom/android/internal/R$styleable;->PopupWindow:[I Lcom/android/internal/R$styleable;->Preference_defaultValue:I Lcom/android/internal/R$styleable;->Preference_dependency:I Lcom/android/internal/R$styleable;->Preference_enabled:I @@ -2765,12 +2991,15 @@ Lcom/android/internal/R$styleable;->SyncAdapter_settingsActivity:I Lcom/android/internal/R$styleable;->SyncAdapter_supportsUploading:I Lcom/android/internal/R$styleable;->SyncAdapter_userVisible:I Lcom/android/internal/R$styleable;->TabWidget:[I +Lcom/android/internal/R$styleable;->TextAppearance:[I Lcom/android/internal/R$styleable;->TextView_drawableBottom:I Lcom/android/internal/R$styleable;->TextView_drawableLeft:I Lcom/android/internal/R$styleable;->TextView_drawableRight:I Lcom/android/internal/R$styleable;->TextView_drawableTop:I Lcom/android/internal/R$styleable;->TextView:[I Lcom/android/internal/R$styleable;->TextView_maxLines:I +Lcom/android/internal/R$styleable;->TextView_textColorHint:I +Lcom/android/internal/R$styleable;->TextView_textColor:I Lcom/android/internal/R$styleable;->View_background:I Lcom/android/internal/R$styleable;->ViewGroup_Layout:[I Lcom/android/internal/R$styleable;->ViewGroup_Layout_layout_height:I @@ -2791,24 +3020,35 @@ Lcom/android/internal/telephony/IPhoneSubInfo$Stub;->asInterface(Landroid/os/IBi Lcom/android/internal/telephony/IPhoneSubInfo$Stub$Proxy;-><init>(Landroid/os/IBinder;)V Lcom/android/internal/telephony/ISms$Stub;->asInterface(Landroid/os/IBinder;)Lcom/android/internal/telephony/ISms; Lcom/android/internal/telephony/ISub$Stub$Proxy;-><init>(Landroid/os/IBinder;)V +Lcom/android/internal/telephony/ITelephony;->answerRingingCall()V Lcom/android/internal/telephony/ITelephony;->call(Ljava/lang/String;Ljava/lang/String;)V +Lcom/android/internal/telephony/ITelephony;->dial(Ljava/lang/String;)V Lcom/android/internal/telephony/ITelephony;->disableDataConnectivity()Z Lcom/android/internal/telephony/ITelephony;->enableDataConnectivity()Z Lcom/android/internal/telephony/ITelephony;->endCall()Z +Lcom/android/internal/telephony/ITelephony;->getCallState()I +Lcom/android/internal/telephony/ITelephony;->getDataState()I Lcom/android/internal/telephony/ITelephony;->isIdle(Ljava/lang/String;)Z Lcom/android/internal/telephony/ITelephony;->silenceRinger()V Lcom/android/internal/telephony/ITelephony$Stub;->asInterface(Landroid/os/IBinder;)Lcom/android/internal/telephony/ITelephony; Lcom/android/internal/telephony/ITelephony$Stub$Proxy;->endCall()Z Lcom/android/internal/telephony/ITelephony$Stub$Proxy;-><init>(Landroid/os/IBinder;)V +Lcom/android/internal/telephony/ITelephony$Stub;->TRANSACTION_call:I +Lcom/android/internal/telephony/ITelephony$Stub;->TRANSACTION_endCall:I Lcom/android/internal/telephony/ITelephony$Stub;->TRANSACTION_getDeviceId:I +Lcom/android/internal/telephony/SmsRawData;->CREATOR:Landroid/os/Parcelable$Creator; +Lcom/android/internal/telephony/SmsRawData;-><init>([B)V Lcom/android/internal/textservice/ITextServicesManager$Stub$Proxy;-><init>(Landroid/os/IBinder;)V Lcom/android/internal/util/AsyncChannel;->sendMessage(III)V Lcom/android/internal/util/FastPrintWriter;-><init>(Ljava/io/OutputStream;)V Lcom/android/internal/util/XmlUtils;->readMapXml(Ljava/io/InputStream;)Ljava/util/HashMap; +Lcom/android/internal/util/XmlUtils;->skipCurrentTag(Lorg/xmlpull/v1/XmlPullParser;)V +Lcom/android/internal/util/XmlUtils;->writeMapXml(Ljava/util/Map;Ljava/io/OutputStream;)V Lcom/android/internal/view/IInputMethodManager$Stub;->asInterface(Landroid/os/IBinder;)Lcom/android/internal/view/IInputMethodManager; Lcom/android/internal/view/IInputMethodManager$Stub$Proxy;->getEnabledInputMethodList()Ljava/util/List; Lcom/android/internal/view/IInputMethodManager$Stub$Proxy;-><init>(Landroid/os/IBinder;)V Lcom/android/internal/view/InputBindResult;->CREATOR:Landroid/os/Parcelable$Creator; +Lcom/android/internal/view/menu/MenuBuilder;-><init>(Landroid/content/Context;)V Lcom/android/internal/view/menu/MenuBuilder;->mContext:Landroid/content/Context; Lcom/android/internal/view/menu/MenuBuilder;->setCurrentMenuInfo(Landroid/view/ContextMenu$ContextMenuInfo;)V Lcom/android/internal/view/menu/MenuBuilder;->setOptionalIconsVisible(Z)V @@ -2864,6 +3104,7 @@ Lcom/android/org/conscrypt/ConscryptSocketBase;->getSoWriteTimeout()I Lcom/android/org/conscrypt/ConscryptSocketBase;->setHandshakeTimeout(I)V Lcom/android/org/conscrypt/ConscryptSocketBase;->setHostname(Ljava/lang/String;)V Lcom/android/org/conscrypt/ConscryptSocketBase;->setSoWriteTimeout(I)V +Lcom/android/org/conscrypt/ConscryptSocketBase;->socket:Ljava/net/Socket; Lcom/android/org/conscrypt/OpenSSLSocketImpl;->getAlpnSelectedProtocol()[B Lcom/android/org/conscrypt/OpenSSLSocketImpl;->getChannelId()[B Lcom/android/org/conscrypt/OpenSSLSocketImpl;->getHostname()Ljava/lang/String; @@ -2886,10 +3127,12 @@ Ldalvik/system/BaseDexClassLoader;->getLdLibraryPath()Ljava/lang/String; Ldalvik/system/BaseDexClassLoader;->pathList:Ldalvik/system/DexPathList; Ldalvik/system/BlockGuard;->getThreadPolicy()Ldalvik/system/BlockGuard$Policy; Ldalvik/system/BlockGuard$Policy;->onNetwork()V +Ldalvik/system/BlockGuard$Policy;->onReadFromDisk()V Ldalvik/system/CloseGuard;->close()V Ldalvik/system/CloseGuard;->get()Ldalvik/system/CloseGuard; Ldalvik/system/CloseGuard;->open(Ljava/lang/String;)V Ldalvik/system/CloseGuard;->warnIfOpen()V +Ldalvik/system/DexFile$DFEnum;->mNameList:[Ljava/lang/String; Ldalvik/system/DexFile;->getClassNameList(Ljava/lang/Object;)[Ljava/lang/String; Ldalvik/system/DexFile;->isBackedByOatFile()Z Ldalvik/system/DexFile;->loadClassBinaryName(Ljava/lang/String;Ljava/lang/ClassLoader;Ljava/util/List;)Ljava/lang/Class; @@ -2898,6 +3141,7 @@ Ldalvik/system/DexFile;->mFileName:Ljava/lang/String; Ldalvik/system/DexFile;->mInternalCookie:Ljava/lang/Object; Ldalvik/system/DexFile;->openDexFile(Ljava/lang/String;Ljava/lang/String;ILjava/lang/ClassLoader;[Ldalvik/system/DexPathList$Element;)Ljava/lang/Object; Ldalvik/system/DexPathList;->addDexPath(Ljava/lang/String;Ljava/io/File;)V +Ldalvik/system/DexPathList;->definingContext:Ljava/lang/ClassLoader; Ldalvik/system/DexPathList;->dexElements:[Ldalvik/system/DexPathList$Element; Ldalvik/system/DexPathList$Element;->dexFile:Ldalvik/system/DexFile; Ldalvik/system/DexPathList$Element;-><init>(Ldalvik/system/DexFile;Ljava/io/File;)V @@ -2918,6 +3162,7 @@ Ldalvik/system/VMDebug;->dumpReferenceTables()V Ldalvik/system/VMRuntime;->addressOf(Ljava/lang/Object;)J Ldalvik/system/VMRuntime;->clearGrowthLimit()V Ldalvik/system/VMRuntime;->getCurrentInstructionSet()Ljava/lang/String; +Ldalvik/system/VMRuntime;->getInstructionSet(Ljava/lang/String;)Ljava/lang/String; Ldalvik/system/VMRuntime;->getRuntime()Ldalvik/system/VMRuntime; Ldalvik/system/VMRuntime;->is64Bit()Z Ldalvik/system/VMRuntime;->newNonMovableArray(Ljava/lang/Class;I)Ljava/lang/Object; @@ -2935,7 +3180,9 @@ Ldalvik/system/VMStack;->getCallingClassLoader()Ljava/lang/ClassLoader; Ldalvik/system/VMStack;->getStackClass2()Ljava/lang/Class; Ljava/io/FileDescriptor;->descriptor:I Ljava/io/FileDescriptor;->getInt$()I +Ljava/io/FileDescriptor;->isSocket$()Z Ljava/io/FileDescriptor;->setInt$(I)V +Ljava/io/File;->fs:Ljava/io/FileSystem; Ljava/io/FileInputStream;->fd:Ljava/io/FileDescriptor; Ljava/io/FileOutputStream;->fd:Ljava/io/FileDescriptor; Ljava/io/ObjectStreamClass;->getConstructorId(Ljava/lang/Class;)J @@ -2945,8 +3192,10 @@ Ljava/lang/AbstractStringBuilder;->value:[C Ljava/lang/Boolean;->value:Z Ljava/lang/Byte;->value:B Ljava/lang/Character;->value:C +Ljava/lang/Class;->accessFlags:I Ljava/lang/Class;->dexCache:Ljava/lang/Object; Ljava/lang/Class;->dexClassDefIndex:I +Ljava/lang/Class;->ifTable:[Ljava/lang/Object; Ljava/lang/ClassLoader;->parent:Ljava/lang/ClassLoader; Ljava/lang/Daemons$Daemon;->isRunning()Z Ljava/lang/Daemons$Daemon;->start()V @@ -3018,6 +3267,7 @@ Ljava/net/Inet6Address$Inet6AddressHolder;->scope_ifname:Ljava/net/NetworkInterf Ljava/net/Inet6Address;-><init>()V Ljava/net/InetAddress;->clearDnsCache()V Ljava/net/InetAddress;->holder:Ljava/net/InetAddress$InetAddressHolder; +Ljava/net/InetAddress;->holder()Ljava/net/InetAddress$InetAddressHolder; Ljava/net/InetAddress$InetAddressHolder;->address:I Ljava/net/InetAddress$InetAddressHolder;->family:I Ljava/net/InetAddress$InetAddressHolder;->hostName:Ljava/lang/String; @@ -3026,6 +3276,8 @@ Ljava/net/InetAddress;->isNumeric(Ljava/lang/String;)Z Ljava/net/InetAddress;->parseNumericAddress(Ljava/lang/String;)Ljava/net/InetAddress; Ljava/net/Socket;->getFileDescriptor$()Ljava/io/FileDescriptor; Ljava/net/Socket;->impl:Ljava/net/SocketImpl; +Ljava/net/SocketImpl;->serverSocket:Ljava/net/ServerSocket; +Ljava/net/SocketImpl;->socket:Ljava/net/Socket; Ljava/net/URI;->host:Ljava/lang/String; Ljava/net/URL;->handler:Ljava/net/URLStreamHandler; Ljava/net/URL;->handlers:Ljava/util/Hashtable; @@ -3060,11 +3312,16 @@ Ljava/util/Collections$SynchronizedMap;->m:Ljava/util/Map; Ljava/util/Collections$UnmodifiableCollection;->c:Ljava/util/Collection; Ljava/util/Collections$UnmodifiableMap;->m:Ljava/util/Map; Ljava/util/concurrent/ConcurrentHashMap$BaseIterator;->hasMoreElements()Z +Ljava/util/concurrent/Executors$RunnableAdapter;->task:Ljava/lang/Runnable; Ljava/util/concurrent/FutureTask;->callable:Ljava/util/concurrent/Callable; +Ljava/util/concurrent/FutureTask;->EXCEPTIONAL:I +Ljava/util/concurrent/FutureTask;->outcome:Ljava/lang/Object; +Ljava/util/concurrent/FutureTask;->state:I Ljava/util/concurrent/LinkedBlockingQueue;->capacity:I Ljava/util/EnumMap;->keyType:Ljava/lang/Class; Ljava/util/EnumSet;->elementType:Ljava/lang/Class; Ljava/util/HashMap$HashIterator;->hasNext()Z +Ljava/util/jar/JarFile;->manifest:Ljava/util/jar/Manifest; Ljava/util/LinkedHashMap;->eldest()Ljava/util/Map$Entry; Ljava/util/LinkedHashMap$LinkedHashIterator;->hasNext()Z Ljava/util/Locale;->createConstant(Ljava/lang/String;Ljava/lang/String;)Ljava/util/Locale; @@ -3087,11 +3344,15 @@ Ljava/util/zip/ZipEntry;-><init>(Ljava/lang/String;Ljava/lang/String;JJJII[BJ)V Ljava/util/zip/ZipFile;->jzfile:J Ljavax/net/ssl/SSLServerSocketFactory;->defaultServerSocketFactory:Ljavax/net/ssl/SSLServerSocketFactory; Ljavax/net/ssl/SSLSocketFactory;->defaultSocketFactory:Ljavax/net/ssl/SSLSocketFactory; +Llibcore/util/BasicLruCache;->map:Ljava/util/LinkedHashMap; Llibcore/util/ZoneInfo;->mTransitions:[J Lorg/apache/http/conn/ssl/SSLSocketFactory;-><init>(Ljavax/net/ssl/SSLSocketFactory;)V Lorg/apache/http/conn/ssl/SSLSocketFactory;-><init>()V +Lorg/ccil/cowan/tagsoup/AttributesImpl;->data:[Ljava/lang/String; +Lorg/ccil/cowan/tagsoup/AttributesImpl;->length:I Lorg/json/JSONArray;->values:Ljava/util/List; Lorg/json/JSONObject;->append(Ljava/lang/String;Ljava/lang/Object;)Lorg/json/JSONObject; Lorg/json/JSONObject;->keySet()Ljava/util/Set; Lorg/json/JSONObject;->writeTo(Lorg/json/JSONStringer;)V +Lorg/w3c/dom/traversal/NodeIterator;->nextNode()Lorg/w3c/dom/Node; Lsun/misc/Unsafe;->theUnsafe:Lsun/misc/Unsafe; diff --git a/core/java/android/app/AppComponentFactory.java b/core/java/android/app/AppComponentFactory.java index 4df737999434..cfaeec9096ed 100644 --- a/core/java/android/app/AppComponentFactory.java +++ b/core/java/android/app/AppComponentFactory.java @@ -36,6 +36,10 @@ public class AppComponentFactory { * Allows application to override the creation of the application object. This can be used to * perform things such as dependency injection or class loader changes to these * classes. + * <p> + * This method is only intended to provide a hook for instantiation. It does not provide + * earlier access to the Application object. The returned object will not be initialized + * as a Context yet and should not be used to interact with other android APIs. * * @param cl The default classloader to use for instantiation. * @param className The class to be instantiated. @@ -50,6 +54,10 @@ public class AppComponentFactory { * Allows application to override the creation of activities. This can be used to * perform things such as dependency injection or class loader changes to these * classes. + * <p> + * This method is only intended to provide a hook for instantiation. It does not provide + * earlier access to the Activity object. The returned object will not be initialized + * as a Context yet and should not be used to interact with other android APIs. * * @param cl The default classloader to use for instantiation. * @param className The class to be instantiated. @@ -80,6 +88,10 @@ public class AppComponentFactory { * Allows application to override the creation of services. This can be used to * perform things such as dependency injection or class loader changes to these * classes. + * <p> + * This method is only intended to provide a hook for instantiation. It does not provide + * earlier access to the Service object. The returned object will not be initialized + * as a Context yet and should not be used to interact with other android APIs. * * @param cl The default classloader to use for instantiation. * @param className The class to be instantiated. @@ -95,6 +107,10 @@ public class AppComponentFactory { * Allows application to override the creation of providers. This can be used to * perform things such as dependency injection or class loader changes to these * classes. + * <p> + * This method is only intended to provide a hook for instantiation. It does not provide + * earlier access to the ContentProvider object. The returned object will not be initialized + * with a Context yet and should not be used to interact with other android APIs. * * @param cl The default classloader to use for instantiation. * @param className The class to be instantiated. @@ -108,5 +124,5 @@ public class AppComponentFactory { /** * @hide */ - public static AppComponentFactory DEFAULT = new AppComponentFactory(); + public static final AppComponentFactory DEFAULT = new AppComponentFactory(); } diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index 3c6f13570d3e..b64aae5240b2 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -2747,110 +2747,6 @@ public class DevicePolicyManager { } /** - * The maximum number of characters allowed in the password blacklist. - */ - private static final int PASSWORD_BLACKLIST_CHARACTER_LIMIT = 128 * 1000; - - /** - * Throws an exception if the password blacklist is too large. - * - * @hide - */ - public static void enforcePasswordBlacklistSize(List<String> blacklist) { - if (blacklist == null) { - return; - } - long characterCount = 0; - for (final String item : blacklist) { - characterCount += item.length(); - } - if (characterCount > PASSWORD_BLACKLIST_CHARACTER_LIMIT) { - throw new IllegalArgumentException("128 thousand blacklist character limit exceeded by " - + (characterCount - PASSWORD_BLACKLIST_CHARACTER_LIMIT) + " characters"); - } - } - - /** - * Called by an application that is administering the device to blacklist passwords. - * <p> - * Any blacklisted password or PIN is prevented from being enrolled by the user or the admin. - * Note that the match against the blacklist is case insensitive. The blacklist applies for all - * password qualities requested by {@link #setPasswordQuality} however it is not taken into - * consideration by {@link #isActivePasswordSufficient}. - * <p> - * The blacklist can be cleared by passing {@code null} or an empty list. The blacklist is - * given a name that is used to track which blacklist is currently set by calling {@link - * #getPasswordBlacklistName}. If the blacklist is being cleared, the name is ignored and {@link - * #getPasswordBlacklistName} will return {@code null}. The name can only be {@code null} when - * the blacklist is being cleared. - * <p> - * The blacklist is limited to a total of 128 thousand characters rather than limiting to a - * number of entries. - * <p> - * This method can be called on the {@link DevicePolicyManager} instance returned by - * {@link #getParentProfileInstance(ComponentName)} in order to set restrictions on the parent - * profile. - * - * @param admin the {@link DeviceAdminReceiver} this request is associated with - * @param name name to associate with the blacklist - * @param blacklist list of passwords to blacklist or {@code null} to clear the blacklist - * @return whether the new blacklist was successfully installed - * @throws SecurityException if {@code admin} is not a device or profile owner - * @throws IllegalArgumentException if the blacklist surpasses the character limit - * @throws NullPointerException if {@code name} is {@code null} when setting a non-empty list - * - * @see #getPasswordBlacklistName - * @see #isActivePasswordSufficient - * @see #resetPasswordWithToken - */ - public boolean setPasswordBlacklist(@NonNull ComponentName admin, @Nullable String name, - @Nullable List<String> blacklist) { - enforcePasswordBlacklistSize(blacklist); - - try { - return mService.setPasswordBlacklist(admin, name, blacklist, mParentInstance); - } catch (RemoteException re) { - throw re.rethrowFromSystemServer(); - } - } - - /** - * Get the name of the password blacklist set by the given admin. - * - * @param admin the {@link DeviceAdminReceiver} this request is associated with - * @return the name of the blacklist or {@code null} if no blacklist is set - * - * @see #setPasswordBlacklist - */ - public @Nullable String getPasswordBlacklistName(@NonNull ComponentName admin) { - try { - return mService.getPasswordBlacklistName(admin, myUserId(), mParentInstance); - } catch (RemoteException re) { - throw re.rethrowFromSystemServer(); - } - } - - /** - * Test if a given password is blacklisted. - * - * @param userId the user to valiate for - * @param password the password to check against the blacklist - * @return whether the password is blacklisted - * - * @see #setPasswordBlacklist - * - * @hide - */ - @RequiresPermission(android.Manifest.permission.TEST_BLACKLISTED_PASSWORD) - public boolean isPasswordBlacklisted(@UserIdInt int userId, @NonNull String password) { - try { - return mService.isPasswordBlacklisted(userId, password); - } catch (RemoteException re) { - throw re.rethrowFromSystemServer(); - } - } - - /** * Determine whether the current password the user has set is sufficient to meet the policy * requirements (e.g. quality, minimum length) that have been requested by the admins of this * user and its participating profiles. Restrictions on profiles that have a separate challenge diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl index 4b39a9a53252..37508cdc1119 100644 --- a/core/java/android/app/admin/IDevicePolicyManager.aidl +++ b/core/java/android/app/admin/IDevicePolicyManager.aidl @@ -79,10 +79,6 @@ interface IDevicePolicyManager { long getPasswordExpiration(in ComponentName who, int userHandle, boolean parent); - boolean setPasswordBlacklist(in ComponentName who, String name, in List<String> blacklist, boolean parent); - String getPasswordBlacklistName(in ComponentName who, int userId, boolean parent); - boolean isPasswordBlacklisted(int userId, String password); - boolean isActivePasswordSufficient(int userHandle, boolean parent); boolean isProfileActivePasswordSufficientForParent(int userHandle); boolean isUsingUnifiedPassword(in ComponentName admin); diff --git a/core/java/android/app/slice/ISliceManager.aidl b/core/java/android/app/slice/ISliceManager.aidl index 74e3c3ee4c81..a2aaf12432f8 100644 --- a/core/java/android/app/slice/ISliceManager.aidl +++ b/core/java/android/app/slice/ISliceManager.aidl @@ -25,7 +25,8 @@ interface ISliceManager { void unpinSlice(String pkg, in Uri uri, in IBinder token); boolean hasSliceAccess(String pkg); SliceSpec[] getPinnedSpecs(in Uri uri, String pkg); - int checkSlicePermission(in Uri uri, String pkg, int pid, int uid); + int checkSlicePermission(in Uri uri, String pkg, int pid, int uid, + in String[] autoGrantPermissions); void grantPermissionFromUser(in Uri uri, String pkg, String callingPkg, boolean allSlices); Uri[] getPinnedSlices(String pkg); diff --git a/core/java/android/app/slice/SliceManager.java b/core/java/android/app/slice/SliceManager.java index 1afe53d8a7b5..d5a98a5d9c28 100644 --- a/core/java/android/app/slice/SliceManager.java +++ b/core/java/android/app/slice/SliceManager.java @@ -81,6 +81,17 @@ public class SliceManager { * An activity can be statically linked to a slice uri by including a meta-data item * for this key that contains a valid slice uri for the same application declaring * the activity. + * + * <pre class="prettyprint"> + * {@literal + * <activity android:name="com.example.mypkg.MyActivity"> + * <meta-data android:name="android.metadata.SLICE_URI" + * android:value="content://com.example.mypkg/main_slice" /> + * </activity>} + * </pre> + * + * @see #mapIntentToUri(Intent) + * @see SliceProvider#onMapIntentToUri(Intent) */ public static final String SLICE_METADATA_KEY = "android.metadata.SLICE_URI"; @@ -257,16 +268,17 @@ public class SliceManager { * <p> * This goes through a several stage resolution process to determine if any slice * can represent this intent. - * - If the intent contains data that {@link ContentResolver#getType} is - * {@link SliceProvider#SLICE_TYPE} then the data will be returned. - * - If the intent with {@link #CATEGORY_SLICE} added resolves to a provider, then + * <ol> + * <li> If the intent contains data that {@link ContentResolver#getType} is + * {@link SliceProvider#SLICE_TYPE} then the data will be returned.</li> + * <li>If the intent with {@link #CATEGORY_SLICE} added resolves to a provider, then * the provider will be asked to {@link SliceProvider#onMapIntentToUri} and that result - * will be returned. - * - Lastly, if the intent explicitly points at an activity, and that activity has + * will be returned.</li> + * <li>Lastly, if the intent explicitly points at an activity, and that activity has * meta-data for key {@link #SLICE_METADATA_KEY}, then the Uri specified there will be - * returned. - * - If no slice is found, then {@code null} is returned. - * + * returned.</li> + * <li>If no slice is found, then {@code null} is returned.</li> + * </ol> * @param intent The intent associated with a slice. * @return The Slice Uri provided by the app or null if none exists. * @see Slice @@ -392,7 +404,8 @@ public class SliceManager { * Does the permission check to see if a caller has access to a specific slice. * @hide */ - public void enforceSlicePermission(Uri uri, String pkg, int pid, int uid) { + public void enforceSlicePermission(Uri uri, String pkg, int pid, int uid, + String[] autoGrantPermissions) { try { if (UserHandle.isSameApp(uid, Process.myUid())) { return; @@ -400,7 +413,7 @@ public class SliceManager { if (pkg == null) { throw new SecurityException("No pkg specified"); } - int result = mService.checkSlicePermission(uri, pkg, pid, uid); + int result = mService.checkSlicePermission(uri, pkg, pid, uid, autoGrantPermissions); if (result == PERMISSION_DENIED) { throw new SecurityException("User " + uid + " does not have slice permission for " + uri + "."); @@ -412,6 +425,8 @@ public class SliceManager { Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_GRANT_PREFIX_URI_PERMISSION); + // Notify a change has happened because we just granted a permission. + mContext.getContentResolver().notifyChange(uri, null); } } catch (RemoteException e) { throw e.rethrowFromSystemServer(); diff --git a/core/java/android/app/slice/SliceProvider.java b/core/java/android/app/slice/SliceProvider.java index bbeb3849be78..fe5742d6da38 100644 --- a/core/java/android/app/slice/SliceProvider.java +++ b/core/java/android/app/slice/SliceProvider.java @@ -149,10 +149,31 @@ public abstract class SliceProvider extends ContentProvider { private static final boolean DEBUG = false; private static final long SLICE_BIND_ANR = 2000; + private final String[] mAutoGrantPermissions; private String mCallback; private SliceManager mSliceManager; + /** + * A version of constructing a SliceProvider that allows autogranting slice permissions + * to apps that hold specific platform permissions. + * <p> + * When an app tries to bind a slice from this provider that it does not have access to, + * This provider will check if the caller holds permissions to any of the autoGrantPermissions + * specified, if they do they will be granted persisted uri access to all slices of this + * provider. + * + * @param autoGrantPermissions List of permissions that holders are auto-granted access + * to slices. + */ + public SliceProvider(@NonNull String... autoGrantPermissions) { + mAutoGrantPermissions = autoGrantPermissions; + } + + public SliceProvider() { + mAutoGrantPermissions = new String[0]; + } + @Override public void attachInfo(Context context, ProviderInfo info) { super.attachInfo(context, info); @@ -402,7 +423,7 @@ public abstract class SliceProvider extends ContentProvider { : getContext().getPackageManager().getNameForUid(callingUid); try { mSliceManager.enforceSlicePermission(sliceUri, pkg, - callingPid, callingUid); + callingPid, callingUid, mAutoGrantPermissions); } catch (SecurityException e) { return createPermissionSlice(getContext(), sliceUri, pkg); } diff --git a/core/java/android/app/usage/NetworkStats.java b/core/java/android/app/usage/NetworkStats.java index da36157d85f8..e315e9115f41 100644 --- a/core/java/android/app/usage/NetworkStats.java +++ b/core/java/android/app/usage/NetworkStats.java @@ -24,7 +24,6 @@ import android.net.NetworkStatsHistory; import android.net.NetworkTemplate; import android.net.TrafficStats; import android.os.RemoteException; -import android.os.ServiceManager; import android.util.IntArray; import android.util.Log; @@ -98,9 +97,8 @@ public final class NetworkStats implements AutoCloseable { /** @hide */ NetworkStats(Context context, NetworkTemplate template, int flags, long startTimestamp, - long endTimestamp) throws RemoteException, SecurityException { - final INetworkStatsService statsService = INetworkStatsService.Stub.asInterface( - ServiceManager.getService(Context.NETWORK_STATS_SERVICE)); + long endTimestamp, INetworkStatsService statsService) + throws RemoteException, SecurityException { // Open network stats session mSession = statsService.openSessionForUsageStats(flags, context.getOpPackageName()); mCloseGuard.open("close"); diff --git a/core/java/android/app/usage/NetworkStatsManager.java b/core/java/android/app/usage/NetworkStatsManager.java index 5576e86edd8a..2357637b7270 100644 --- a/core/java/android/app/usage/NetworkStatsManager.java +++ b/core/java/android/app/usage/NetworkStatsManager.java @@ -37,6 +37,8 @@ import android.os.ServiceManager; import android.os.ServiceManager.ServiceNotFoundException; import android.util.Log; +import com.android.internal.annotations.VisibleForTesting; + /** * Provides access to network usage history and statistics. Usage data is collected in * discrete bins of time called 'Buckets'. See {@link NetworkStats.Bucket} for details. @@ -107,9 +109,15 @@ public class NetworkStatsManager { * {@hide} */ public NetworkStatsManager(Context context) throws ServiceNotFoundException { + this(context, INetworkStatsService.Stub.asInterface( + ServiceManager.getServiceOrThrow(Context.NETWORK_STATS_SERVICE))); + } + + /** @hide */ + @VisibleForTesting + public NetworkStatsManager(Context context, INetworkStatsService service) { mContext = context; - mService = INetworkStatsService.Stub.asInterface( - ServiceManager.getServiceOrThrow(Context.NETWORK_STATS_SERVICE)); + mService = service; setPollOnOpen(true); } @@ -135,7 +143,8 @@ public class NetworkStatsManager { public Bucket querySummaryForDevice(NetworkTemplate template, long startTime, long endTime) throws SecurityException, RemoteException { Bucket bucket = null; - NetworkStats stats = new NetworkStats(mContext, template, mFlags, startTime, endTime); + NetworkStats stats = new NetworkStats(mContext, template, mFlags, startTime, endTime, + mService); bucket = stats.getDeviceSummaryForNetwork(); stats.close(); @@ -208,7 +217,7 @@ public class NetworkStatsManager { } NetworkStats stats; - stats = new NetworkStats(mContext, template, mFlags, startTime, endTime); + stats = new NetworkStats(mContext, template, mFlags, startTime, endTime, mService); stats.startSummaryEnumeration(); stats.close(); @@ -245,7 +254,7 @@ public class NetworkStatsManager { } NetworkStats result; - result = new NetworkStats(mContext, template, mFlags, startTime, endTime); + result = new NetworkStats(mContext, template, mFlags, startTime, endTime, mService); result.startSummaryEnumeration(); return result; @@ -295,7 +304,7 @@ public class NetworkStatsManager { NetworkStats result; try { - result = new NetworkStats(mContext, template, mFlags, startTime, endTime); + result = new NetworkStats(mContext, template, mFlags, startTime, endTime, mService); result.startHistoryEnumeration(uid, tag); } catch (RemoteException e) { Log.e(TAG, "Error while querying stats for uid=" + uid + " tag=" + tag, e); @@ -341,7 +350,7 @@ public class NetworkStatsManager { } NetworkStats result; - result = new NetworkStats(mContext, template, mFlags, startTime, endTime); + result = new NetworkStats(mContext, template, mFlags, startTime, endTime, mService); result.startUserUidEnumeration(); return result; } @@ -451,19 +460,20 @@ public class NetworkStatsManager { } private static NetworkTemplate createTemplate(int networkType, String subscriberId) { - NetworkTemplate template = null; + final NetworkTemplate template; switch (networkType) { - case ConnectivityManager.TYPE_MOBILE: { - template = NetworkTemplate.buildTemplateMobileAll(subscriberId); - } break; - case ConnectivityManager.TYPE_WIFI: { + case ConnectivityManager.TYPE_MOBILE: + template = subscriberId == null + ? NetworkTemplate.buildTemplateMobileWildcard() + : NetworkTemplate.buildTemplateMobileAll(subscriberId); + break; + case ConnectivityManager.TYPE_WIFI: template = NetworkTemplate.buildTemplateWifiWildcard(); - } break; - default: { + break; + default: throw new IllegalArgumentException("Cannot create template for network type " + networkType + ", subscriberId '" + NetworkIdentity.scrubSubscriberId(subscriberId) + "'."); - } } return template; } diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java index e07f586a5701..80b1c3dfa06d 100644 --- a/core/java/android/net/ConnectivityManager.java +++ b/core/java/android/net/ConnectivityManager.java @@ -861,6 +861,10 @@ public class ConnectivityManager { * You should always check {@link NetworkInfo#isConnected()} before initiating * network traffic. This may return {@code null} when there is no default * network. + * Note that if the default network is a VPN, this method will return the + * NetworkInfo for one of its underlying networks instead, or null if the + * VPN agent did not specify any. Apps interested in learning about VPNs + * should use {@link #getNetworkInfo(android.net.Network)} instead. * * @return a {@link NetworkInfo} object for the current default network * or {@code null} if no default network is currently active @@ -1018,7 +1022,11 @@ public class ConnectivityManager { * which you're interested. * @return a {@link NetworkInfo} object for the requested * network type or {@code null} if the type is not - * supported by the device. + * supported by the device. If {@code networkType} is + * TYPE_VPN and a VPN is active for the calling app, + * then this method will try to return one of the + * underlying networks for the VPN or null if the + * VPN agent didn't specify any. * * @deprecated This method does not support multiple connected networks * of the same type. Use {@link #getAllNetworks} and diff --git a/core/java/android/net/Network.java b/core/java/android/net/Network.java index d24c131242d6..15a0ee5eb2a6 100644 --- a/core/java/android/net/Network.java +++ b/core/java/android/net/Network.java @@ -26,6 +26,8 @@ import android.util.proto.ProtoOutputStream; import com.android.okhttp.internalandroidapi.Dns; import com.android.okhttp.internalandroidapi.HttpURLConnectionFactory; +import libcore.io.IoUtils; + import java.io.FileDescriptor; import java.io.IOException; import java.net.DatagramSocket; @@ -142,9 +144,15 @@ public class Network implements Parcelable { for (int i = 0; i < hostAddresses.length; i++) { try { Socket socket = createSocket(); - if (localAddress != null) socket.bind(localAddress); - socket.connect(new InetSocketAddress(hostAddresses[i], port)); - return socket; + boolean failed = true; + try { + if (localAddress != null) socket.bind(localAddress); + socket.connect(new InetSocketAddress(hostAddresses[i], port)); + failed = false; + return socket; + } finally { + if (failed) IoUtils.closeQuietly(socket); + } } catch (IOException e) { if (i == (hostAddresses.length - 1)) throw e; } @@ -161,15 +169,27 @@ public class Network implements Parcelable { public Socket createSocket(InetAddress address, int port, InetAddress localAddress, int localPort) throws IOException { Socket socket = createSocket(); - socket.bind(new InetSocketAddress(localAddress, localPort)); - socket.connect(new InetSocketAddress(address, port)); + boolean failed = true; + try { + socket.bind(new InetSocketAddress(localAddress, localPort)); + socket.connect(new InetSocketAddress(address, port)); + failed = false; + } finally { + if (failed) IoUtils.closeQuietly(socket); + } return socket; } @Override public Socket createSocket(InetAddress host, int port) throws IOException { Socket socket = createSocket(); - socket.connect(new InetSocketAddress(host, port)); + boolean failed = true; + try { + socket.connect(new InetSocketAddress(host, port)); + failed = false; + } finally { + if (failed) IoUtils.closeQuietly(socket); + } return socket; } @@ -181,7 +201,13 @@ public class Network implements Parcelable { @Override public Socket createSocket() throws IOException { Socket socket = new Socket(); - bindSocket(socket); + boolean failed = true; + try { + bindSocket(socket); + failed = false; + } finally { + if (failed) IoUtils.closeQuietly(socket); + } return socket; } } diff --git a/core/java/android/net/NetworkCapabilities.java b/core/java/android/net/NetworkCapabilities.java index ff5714b38212..374b3abcb641 100644 --- a/core/java/android/net/NetworkCapabilities.java +++ b/core/java/android/net/NetworkCapabilities.java @@ -17,6 +17,7 @@ package android.net; import android.annotation.IntDef; +import android.annotation.SystemApi; import android.net.ConnectivityManager.NetworkCallback; import android.os.Parcel; import android.os.Parcelable; @@ -276,6 +277,7 @@ public final class NetworkCapabilities implements Parcelable { * this network can be used by system apps to upload telemetry data. * @hide */ + @SystemApi public static final int NET_CAPABILITY_OEM_PAID = 22; private static final int MIN_NET_CAPABILITY = NET_CAPABILITY_MMS; diff --git a/core/java/android/net/NetworkRequest.java b/core/java/android/net/NetworkRequest.java index 4f92fa6a7340..caefd896ef16 100644 --- a/core/java/android/net/NetworkRequest.java +++ b/core/java/android/net/NetworkRequest.java @@ -17,6 +17,8 @@ package android.net; import android.annotation.NonNull; +import android.net.NetworkCapabilities.NetCapability; +import android.net.NetworkCapabilities.Transport; import android.os.Parcel; import android.os.Parcelable; import android.os.Process; @@ -427,6 +429,20 @@ public class NetworkRequest implements Parcelable { return type == Type.BACKGROUND_REQUEST; } + /** + * @see Builder#addCapability(int) + */ + public boolean hasCapability(@NetCapability int capability) { + return networkCapabilities.hasCapability(capability); + } + + /** + * @see Builder#addTransportType(int) + */ + public boolean hasTransport(@Transport int transportType) { + return networkCapabilities.hasTransport(transportType); + } + public String toString() { return "NetworkRequest [ " + type + " id=" + requestId + (legacyType != ConnectivityManager.TYPE_NONE ? ", legacyType=" + legacyType : "") + diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index b7f6cdef20ed..b0367dc3f238 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -11061,7 +11061,14 @@ public final class Settings { */ public static final String LOW_POWER_MODE_TRIGGER_LEVEL_MAX = "low_power_trigger_level_max"; - /** + /** + * See com.android.settingslib.fuelgauge.BatterySaverUtils. + * @hide + */ + public static final String LOW_POWER_MODE_SUGGESTION_PARAMS = + "low_power_mode_suggestion_params"; + + /** * If not 0, the activity manager will aggressively finish activities and * processes as soon as they are no longer needed. If 0, the normal * extended lifetime is used. diff --git a/core/java/android/security/keymaster/KeymasterDefs.java b/core/java/android/security/keymaster/KeymasterDefs.java index 1d1333504350..f4dcce1e7e58 100644 --- a/core/java/android/security/keymaster/KeymasterDefs.java +++ b/core/java/android/security/keymaster/KeymasterDefs.java @@ -75,6 +75,7 @@ public final class KeymasterDefs { public static final int KM_TAG_ALLOW_WHILE_ON_BODY = KM_BOOL | 506; public static final int KM_TAG_TRUSTED_USER_PRESENCE_REQUIRED = KM_BOOL | 507; public static final int KM_TAG_TRUSTED_CONFIRMATION_REQUIRED = KM_BOOL | 508; + public static final int KM_TAG_UNLOCKED_DEVICE_REQUIRED = KM_BOOL | 509; public static final int KM_TAG_ALL_APPLICATIONS = KM_BOOL | 600; public static final int KM_TAG_APPLICATION_ID = KM_BYTES | 601; @@ -216,6 +217,7 @@ public final class KeymasterDefs { public static final int KM_ERROR_MISSING_MIN_MAC_LENGTH = -58; public static final int KM_ERROR_UNSUPPORTED_MIN_MAC_LENGTH = -59; public static final int KM_ERROR_CANNOT_ATTEST_IDS = -66; + public static final int KM_ERROR_DEVICE_LOCKED = -72; public static final int KM_ERROR_UNIMPLEMENTED = -100; public static final int KM_ERROR_VERSION_MISMATCH = -101; public static final int KM_ERROR_UNKNOWN_ERROR = -1000; @@ -262,6 +264,7 @@ public final class KeymasterDefs { sErrorCodeToString.put(KM_ERROR_INVALID_MAC_LENGTH, "Invalid MAC or authentication tag length"); sErrorCodeToString.put(KM_ERROR_CANNOT_ATTEST_IDS, "Unable to attest device ids"); + sErrorCodeToString.put(KM_ERROR_DEVICE_LOCKED, "Device locked"); sErrorCodeToString.put(KM_ERROR_UNIMPLEMENTED, "Not implemented"); sErrorCodeToString.put(KM_ERROR_UNKNOWN_ERROR, "Unknown error"); } diff --git a/core/java/android/security/keystore/RecoveryController.java b/core/java/android/security/keystore/RecoveryController.java index d50424db4e56..741af1297763 100644 --- a/core/java/android/security/keystore/RecoveryController.java +++ b/core/java/android/security/keystore/RecoveryController.java @@ -443,16 +443,7 @@ public class RecoveryController { */ public byte[] generateAndStoreKey(@NonNull String alias) throws InternalRecoveryServiceException, LockScreenRequiredException { - try { - return mBinder.generateAndStoreKey(alias); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } catch (ServiceSpecificException e) { - if (e.errorCode == ERROR_INSECURE_USER) { - throw new LockScreenRequiredException(e.getMessage()); - } - throw wrapUnexpectedServiceSpecificException(e); - } + throw new UnsupportedOperationException(); } /** diff --git a/core/java/android/security/keystore/recovery/KeyChainSnapshot.java b/core/java/android/security/keystore/recovery/KeyChainSnapshot.java index e46c34c85d55..9334aa99c86c 100644 --- a/core/java/android/security/keystore/recovery/KeyChainSnapshot.java +++ b/core/java/android/security/keystore/recovery/KeyChainSnapshot.java @@ -78,23 +78,8 @@ public final class KeyChainSnapshot implements Parcelable { private byte[] mEncryptedRecoveryKeyBlob; /** - * @hide - * Deprecated, consider using builder. + * Use builder to create an instance of the class. */ - public KeyChainSnapshot( - int snapshotVersion, - @NonNull List<KeyChainProtectionParams> keyChainProtectionParams, - @NonNull List<WrappedApplicationKey> wrappedApplicationKeys, - @NonNull byte[] encryptedRecoveryKeyBlob) { - mSnapshotVersion = snapshotVersion; - mKeyChainProtectionParams = - Preconditions.checkCollectionElementsNotNull(keyChainProtectionParams, - "KeyChainProtectionParams"); - mEntryRecoveryData = Preconditions.checkCollectionElementsNotNull(wrappedApplicationKeys, - "wrappedApplicationKeys"); - mEncryptedRecoveryKeyBlob = Preconditions.checkNotNull(encryptedRecoveryKeyBlob); - } - private KeyChainSnapshot() { } @@ -108,7 +93,7 @@ public final class KeyChainSnapshot implements Parcelable { } /** - * Number of user secret guesses allowed during Keychain recovery. + * Number of user secret guesses allowed during KeyChain recovery. */ public int getMaxAttempts() { return mMaxAttempts; diff --git a/core/java/android/security/keystore/recovery/KeyDerivationParams.java b/core/java/android/security/keystore/recovery/KeyDerivationParams.java index fd80bb0bbe57..5165f0c30a36 100644 --- a/core/java/android/security/keystore/recovery/KeyDerivationParams.java +++ b/core/java/android/security/keystore/recovery/KeyDerivationParams.java @@ -106,7 +106,7 @@ public final class KeyDerivationParams implements Parcelable { /** * @hide */ - KeyDerivationParams(@KeyDerivationAlgorithm int algorithm, @NonNull byte[] salt, + private KeyDerivationParams(@KeyDerivationAlgorithm int algorithm, @NonNull byte[] salt, int memoryDifficulty) { mAlgorithm = algorithm; mSalt = Preconditions.checkNotNull(salt); diff --git a/core/java/android/security/keystore/recovery/RecoveryController.java b/core/java/android/security/keystore/recovery/RecoveryController.java index ab52d32ce963..a006fa6ab2ef 100644 --- a/core/java/android/security/keystore/recovery/RecoveryController.java +++ b/core/java/android/security/keystore/recovery/RecoveryController.java @@ -41,21 +41,133 @@ import java.util.List; import java.util.Map; /** - * An assistant for generating {@link javax.crypto.SecretKey} instances that can be recovered by - * other Android devices belonging to the user. The exported keychain is protected by the user's - * lock screen. + * Backs up cryptographic keys to remote secure hardware, encrypted with the user's lock screen. * - * <p>The RecoveryController must be paired with a recovery agent. The recovery agent is responsible - * for transporting the keychain to remote trusted hardware. This hardware must prevent brute force - * attempts against the user's lock screen by limiting the number of allowed guesses (to, e.g., 10). - * After that number of incorrect guesses, the trusted hardware no longer allows access to the - * key chain. + * <p>A system app with the {@link android.Manifest#RECOVER_KEYSTORE} permission may generate or + * import recoverable keys using this class. To generate a key, the app must call + * {@link #generateKey(String)} with the desired alias for the key. This returns an AndroidKeyStore + * reference to a 256-bit {@link javax.crypto.SecretKey}, which can be used for AES/GCM/NoPadding. + * In order to get the same key again at a later time, the app can call {@link #getKey(String)} with + * the same alias. If a key is generated in this way the key's raw material is never directly + * exposed to the calling app. The system app may also import key material using + * {@link #importKey(String, byte[])}. The app may only generate and import keys for its own + * {@code uid}. * - * <p>Only the recovery agent itself is able to create keys, so it is expected that the recovery - * agent is itself the system app. + * <p>The same system app must also register a Recovery Agent to manage syncing recoverable keys to + * remote secure hardware. The Recovery Agent is a service that registers itself with the controller + * as follows: * - * <p>A recovery agent requires the privileged permission - * {@code android.Manifest.permission#RECOVER_KEYSTORE}. + * <ul> + * <li>Invokes {@link #initRecoveryService(String, byte[], byte[])} + * <ul> + * <li>The first argument is the alias of the root certificate used to verify trusted + * hardware modules. Each trusted hardware module must have a public key signed with this + * root of trust. Roots of trust must be shipped with the framework. The app can list all + * valid roots of trust by calling {@link #getRootCertificates()}. + * <li>The second argument is the UTF-8 bytes of the XML listing file. It lists the X509 + * certificates containing the public keys of all available remote trusted hardware modules. + * Each of the X509 certificates can be validated against the chosen root of trust. + * <li>The third argument is the UTF-8 bytes of the XML signing file. The file contains a + * signature of the XML listing file. The signature can be validated against the chosen root + * of trust. + * </ul> + * <p>This will cause the controller to choose a random public key from the list. From then + * on the controller will attempt to sync the key chain with the trusted hardware module to whom + * that key belongs. + * <li>Invokes {@link #setServerParams(byte[])} with a byte string that identifies the device + * to a remote server. This server may act as the front-end to the trusted hardware modules. It + * is up to the Recovery Agent to decide how best to identify devices, but this could be, e.g., + * based on the <a href="https://developers.google.com/instance-id/">Instance ID</a> of the + * system app. + * <li>Invokes {@link #setRecoverySecretTypes(int[])} with a list of types of secret used to + * secure the recoverable key chain. For now only + * {@link KeyChainProtectionParams#TYPE_LOCKSCREEN} is supported. + * <li>Invokes {@link #setSnapshotCreatedPendingIntent(PendingIntent)} with a + * {@link PendingIntent} that is to be invoked whenever a new snapshot is created. Although the + * controller can create snapshots without the Recovery Agent registering this intent, it is a + * good idea to register the intent so that the Recovery Agent is able to sync this snapshot to + * the trusted hardware module as soon as it is available. + * </ul> + * + * <p>The trusted hardware module's public key MUST be generated on secure hardware with protections + * equivalent to those described in the + * <a href="https://developer.android.com/preview/features/security/ckv-whitepaper.html">Google + * Cloud Key Vault Service whitepaper</a>. The trusted hardware module itself must protect the key + * chain from brute-forcing using the methods also described in the whitepaper: i.e., it should + * limit the number of allowed attempts to enter the lock screen. If the number of attempts is + * exceeded the key material must no longer be recoverable. + * + * <p>A recoverable key chain snapshot is considered pending if any of the following conditions + * are met: + * + * <ul> + * <li>The system app mutates the key chain. i.e., generates, imports, or removes a key. + * <li>The user changes their lock screen. + * </ul> + * + * <p>Whenever the user unlocks their device, if a snapshot is pending, the Recovery Controller + * generates a new snapshot. It follows these steps to do so: + * + * <ul> + * <li>Generates a 256-bit AES key using {@link java.security.SecureRandom}. This is the + * Recovery Key. + * <li>Wraps the key material of all keys in the recoverable key chain with the Recovery Key. + * <li>Encrypts the Recovery Key with both the public key of the trusted hardware module and a + * symmetric key derived from the user's lock screen. + * </ul> + * + * <p>The controller then writes this snapshot to disk, and uses the {@link PendingIntent} that was + * set by the Recovery Agent during initialization to inform it that a new snapshot is available. + * The snapshot only contains keys for that Recovery Agent's {@code uid} - i.e., keys the agent's + * app itself generated. If multiple Recovery Agents exist on the device, each will be notified of + * their new snapshots, and each snapshots' keys will be only those belonging to the same + * {@code uid}. + * + * <p>The Recovery Agent retrieves its most recent snapshot by calling + * {@link #getKeyChainSnapshot()}. It syncs the snapshot to the remote server. The snapshot contains + * the public key used for encryption, which the server uses to forward the encrypted recovery key + * to the correct trusted hardware module. The snapshot also contains the server params, which are + * used to identify this device to the server. + * + * <p>The client uses the server params to identify a device whose key chain it wishes to restore. + * This may be on a different device to the device that originally synced the key chain. The client + * sends the server params identifying the previous device to the server. The server returns the + * X509 certificate identifying the trusted hardware module in which the encrypted Recovery Key is + * stored. It also returns some vault parameters identifying that particular Recovery Key to the + * trusted hardware module. And it also returns a vault challenge, which is used as part of the + * vault opening protocol to ensure the recovery claim is fresh. See the whitepaper for more + * details. + * + * <p>The key chain is recovered via a {@link RecoverySession}. A Recovery Agent creates one by + * invoking {@link #createRecoverySession()}. It then invokes + * {@link RecoverySession#start(String, CertPath, byte[], byte[], List)} with these arguments: + * + * <ul> + * <li>The alias of the root of trust used to verify the trusted hardware module. + * <li>The X509 certificate of the trusted hardware module. + * <li>The vault parameters used to identify the Recovery Key to the trusted hardware module. + * <li>The vault challenge, as issued by the trusted hardware module. + * <li>A list of secrets, corresponding to the secrets used to protect the key chain. At the + * moment this is a single {@link KeyChainProtectionParams} containing the lock screen of the + * device whose key chain is to be recovered. + * </ul> + * + * <p>This method returns a byte array containing the Recovery Claim, which can be issued to the + * remote trusted hardware module. It is encrypted with the trusted hardware module's public key + * (which has itself been certified with the root of trust). It also contains an ephemeral symmetric + * key generated for this recovery session, which the remote trusted hardware module uses to encrypt + * its responses. This is the Session Key. + * + * <p>If the lock screen provided is correct, the remote trusted hardware module decrypts one of the + * layers of lock-screen encryption from the Recovery Key. It then returns this key, encrypted with + * the Session Key to the Recovery Agent. As the Recovery Agent does not know the Session Key, it + * must then invoke {@link RecoverySession#recoverKeyChainSnapshot(byte[], List)} with the encrypted + * Recovery Key and the list of wrapped application keys. The controller then decrypts the layer of + * encryption provided by the Session Key, and uses the lock screen to decrypt the final layer of + * encryption. It then uses the Recovery Key to decrypt all of the wrapped application keys, and + * imports them into its own KeyStore. The Recovery Agent's app may then access these keys by + * calling {@link #getKey(String)}. Only this app's {@code uid} may access the keys that have been + * recovered. * * @hide */ @@ -465,16 +577,7 @@ public class RecoveryController { @RequiresPermission(android.Manifest.permission.RECOVER_KEYSTORE) public byte[] generateAndStoreKey(@NonNull String alias, byte[] account) throws InternalRecoveryServiceException, LockScreenRequiredException { - try { - return mBinder.generateAndStoreKey(alias); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } catch (ServiceSpecificException e) { - if (e.errorCode == ERROR_INSECURE_USER) { - throw new LockScreenRequiredException(e.getMessage()); - } - throw wrapUnexpectedServiceSpecificException(e); - } + throw new UnsupportedOperationException("Operation is not supported, use generateKey"); } /** diff --git a/core/java/android/security/keystore/recovery/RecoverySession.java b/core/java/android/security/keystore/recovery/RecoverySession.java index 0690bd5fb612..80845d9e0491 100644 --- a/core/java/android/security/keystore/recovery/RecoverySession.java +++ b/core/java/android/security/keystore/recovery/RecoverySession.java @@ -157,8 +157,8 @@ public class RecoverySession implements AutoCloseable { * @param vaultChallenge Data passed from server for this recovery session and used to prevent * replay attacks. * @param secrets Secrets provided by user, the method only uses type and secret fields. - * @return The recovery claim. Claim provides a b binary blob with recovery claim. It is - * encrypted with verifierPublicKey and contains a proof of user secrets, session symmetric + * @return The binary blob with recovery claim. It is encrypted with verifierPublicKey + * and contains a proof of user secrets possession, session symmetric * key and parameters necessary to identify the counter with the number of failed recovery * attempts. * @throws CertificateException if the {@code verifierCertPath} is invalid. @@ -228,7 +228,8 @@ public class RecoverySession implements AutoCloseable { * * @param recoveryKeyBlob Recovery blob encrypted by symmetric key generated for this session. * @param applicationKeys Application keys. Key material can be decrypted using recoveryKeyBlob - * and session. + * and session key generated by {@link #start}. + * @return {@code Map} from recovered keys aliases to their references. * @throws SessionExpiredException if {@code session} has since been closed. * @throws DecryptionFailedException if unable to decrypt the snapshot. * @throws InternalRecoveryServiceException if an error occurs internal to the recovery service. @@ -288,8 +289,7 @@ public class RecoverySession implements AutoCloseable { } /** - * Deletes all data associated with {@code session}. Should not be invoked directly but via - * {@link RecoverySession#close()}. + * Deletes all data associated with {@code session}. */ @RequiresPermission(android.Manifest.permission.RECOVER_KEYSTORE) @Override diff --git a/core/java/android/service/notification/ScheduleCalendar.java b/core/java/android/service/notification/ScheduleCalendar.java index 8a7ff4da26e3..01287104f9b2 100644 --- a/core/java/android/service/notification/ScheduleCalendar.java +++ b/core/java/android/service/notification/ScheduleCalendar.java @@ -144,7 +144,8 @@ public class ScheduleCalendar { } return mSchedule.exitAtAlarm && mSchedule.nextAlarm != 0 - && time >= mSchedule.nextAlarm; + && time >= mSchedule.nextAlarm + && isInSchedule(mSchedule.nextAlarm); } private boolean isInSchedule(int daysOffset, long time, long start, long end) { diff --git a/core/java/android/util/apk/ApkVerityBuilder.java b/core/java/android/util/apk/ApkVerityBuilder.java index 3b8fc5c53d2a..f15e1a1a7c52 100644 --- a/core/java/android/util/apk/ApkVerityBuilder.java +++ b/core/java/android/util/apk/ApkVerityBuilder.java @@ -72,22 +72,31 @@ abstract class ApkVerityBuilder { signatureInfo.centralDirOffset - signatureInfo.apkSigningBlockOffset; long dataSize = apk.length() - signingBlockSize; int[] levelOffset = calculateVerityLevelOffset(dataSize); + int merkleTreeSize = levelOffset[levelOffset.length - 1]; ByteBuffer output = bufferFactory.create( - CHUNK_SIZE_BYTES + // fsverity header + extensions + padding - levelOffset[levelOffset.length - 1]); // Merkle tree size + merkleTreeSize + + CHUNK_SIZE_BYTES); // maximum size of fsverity metadata output.order(ByteOrder.LITTLE_ENDIAN); - ByteBuffer header = slice(output, 0, FSVERITY_HEADER_SIZE_BYTES); - ByteBuffer extensions = slice(output, FSVERITY_HEADER_SIZE_BYTES, CHUNK_SIZE_BYTES); - ByteBuffer tree = slice(output, CHUNK_SIZE_BYTES, output.limit()); + ByteBuffer tree = slice(output, 0, merkleTreeSize); + ByteBuffer header = slice(output, merkleTreeSize, + merkleTreeSize + FSVERITY_HEADER_SIZE_BYTES); + ByteBuffer extensions = slice(output, merkleTreeSize + FSVERITY_HEADER_SIZE_BYTES, + merkleTreeSize + CHUNK_SIZE_BYTES); byte[] apkDigestBytes = new byte[DIGEST_SIZE_BYTES]; ByteBuffer apkDigest = ByteBuffer.wrap(apkDigestBytes); apkDigest.order(ByteOrder.LITTLE_ENDIAN); + // NB: Buffer limit is set inside once finished. calculateFsveritySignatureInternal(apk, signatureInfo, tree, apkDigest, header, extensions); - output.rewind(); + // Put the reverse offset to fs-verity header at the end. + output.position(merkleTreeSize + FSVERITY_HEADER_SIZE_BYTES + extensions.limit()); + output.putInt(FSVERITY_HEADER_SIZE_BYTES + extensions.limit() + + 4); // size of this integer right before EOF + output.flip(); + return new ApkVerityResult(output, apkDigestBytes); } @@ -101,7 +110,8 @@ abstract class ApkVerityBuilder { ByteBuffer verityBlock = ByteBuffer.allocate(CHUNK_SIZE_BYTES) .order(ByteOrder.LITTLE_ENDIAN); ByteBuffer header = slice(verityBlock, 0, FSVERITY_HEADER_SIZE_BYTES); - ByteBuffer extensions = slice(verityBlock, FSVERITY_HEADER_SIZE_BYTES, CHUNK_SIZE_BYTES); + ByteBuffer extensions = slice(verityBlock, FSVERITY_HEADER_SIZE_BYTES, + CHUNK_SIZE_BYTES - FSVERITY_HEADER_SIZE_BYTES); calculateFsveritySignatureInternal(apk, signatureInfo, null, null, header, extensions); @@ -328,10 +338,10 @@ abstract class ApkVerityBuilder { buffer.put((byte) 12); // log2(block-size): log2(4096) buffer.put((byte) 7); // log2(leaves-per-node): log2(4096 / 32) - buffer.putShort((short) 1); // meta algorithm, SHA256_MODE == 1 - buffer.putShort((short) 1); // data algorithm, SHA256_MODE == 1 + buffer.putShort((short) 1); // meta algorithm, SHA256 == 1 + buffer.putShort((short) 1); // data algorithm, SHA256 == 1 - buffer.putInt(0x0); // flags + buffer.putInt(0); // flags buffer.putInt(0); // reserved buffer.putLong(fileSize); // original file size @@ -362,12 +372,11 @@ abstract class ApkVerityBuilder { // // struct fsverity_extension_patch { // __le64 offset; - // u8 length; - // u8 reserved[7]; // u8 databytes[]; // }; final int kSizeOfFsverityExtensionHeader = 8; + final int kExtensionSizeAlignment = 8; { // struct fsverity_extension #1 @@ -385,24 +394,25 @@ abstract class ApkVerityBuilder { { // struct fsverity_extension #2 - final int kSizeOfFsverityPatchExtension = - 8 + // offset size - 1 + // size of length from offset (up to 255) - 7 + // reserved - ZIP_EOCD_CENTRAL_DIR_OFFSET_FIELD_SIZE; - final int kPadding = (int) divideRoundup(kSizeOfFsverityPatchExtension % 8, 8); + final int kTotalSize = kSizeOfFsverityExtensionHeader + + 8 // offset size + + ZIP_EOCD_CENTRAL_DIR_OFFSET_FIELD_SIZE; - buffer.putShort((short) // total size of extension, padded to 64-bit alignment - (kSizeOfFsverityExtensionHeader + kSizeOfFsverityPatchExtension + kPadding)); + buffer.putShort((short) kTotalSize); buffer.put((byte) 1); // ID of patch extension skip(buffer, 5); // reserved // struct fsverity_extension_patch - buffer.putLong(eocdOffset); // offset - buffer.put((byte) ZIP_EOCD_CENTRAL_DIR_OFFSET_FIELD_SIZE); // length - skip(buffer, 7); // reserved - buffer.putInt(Math.toIntExact(signingBlockOffset)); // databytes - skip(buffer, kPadding); // padding + buffer.putLong(eocdOffset + ZIP_EOCD_CENTRAL_DIR_OFFSET_FIELD_OFFSET); // offset + buffer.putInt(Math.toIntExact(signingBlockOffset)); // databytes + + // The extension needs to be 0-padded at the end, since the length may not be multiple + // of 8. + int kPadding = kExtensionSizeAlignment - kTotalSize % kExtensionSizeAlignment; + if (kPadding == kExtensionSizeAlignment) { + kPadding = 0; + } + skip(buffer, kPadding); // padding } buffer.flip(); diff --git a/core/java/com/android/internal/widget/ILockSettings.aidl b/core/java/com/android/internal/widget/ILockSettings.aidl index 59b14f18eb07..ae7ba1948c00 100644 --- a/core/java/com/android/internal/widget/ILockSettings.aidl +++ b/core/java/com/android/internal/widget/ILockSettings.aidl @@ -61,7 +61,6 @@ interface ILockSettings { void initRecoveryServiceWithSigFile(in String rootCertificateAlias, in byte[] recoveryServiceCertFile, in byte[] recoveryServiceSigFile); KeyChainSnapshot getKeyChainSnapshot(); - byte[] generateAndStoreKey(String alias); String generateKey(String alias); String importKey(String alias, in byte[] keyBytes); String getKey(String alias); diff --git a/core/java/com/android/internal/widget/LockPatternView.java b/core/java/com/android/internal/widget/LockPatternView.java index 51dd92961f54..e8fc5989354a 100644 --- a/core/java/com/android/internal/widget/LockPatternView.java +++ b/core/java/com/android/internal/widget/LockPatternView.java @@ -1000,6 +1000,11 @@ public class LockPatternView extends View { setPatternInProgress(false); cancelLineAnimations(); notifyPatternDetected(); + // Also clear pattern if fading is enabled + if (mFadePattern) { + clearPatternDrawLookup(); + mPatternDisplayMode = DisplayMode.Correct; + } invalidate(); } if (PROFILE_DRAWING) { diff --git a/core/proto/android/os/incident.proto b/core/proto/android/os/incident.proto index 0fea0dc1c428..ea0b825b01a8 100644 --- a/core/proto/android/os/incident.proto +++ b/core/proto/android/os/incident.proto @@ -175,9 +175,9 @@ message IncidentProto { ]; optional GZippedFileProto last_kmsg = 2007 [ - (section).type = SECTION_NONE, // disable until selinux permission is gained + (section).type = SECTION_GZIP, (section).args = "/sys/fs/pstore/console-ramoops /sys/fs/pstore/console-ramoops-0 /proc/last_kmsg", - (privacy).dest = DEST_AUTOMATIC + (privacy).dest = DEST_EXPLICIT ]; // System Services diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index 66d25df41e63..2a6b33108881 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -30,33 +30,32 @@ <item><xliff:g id="id">@string/status_bar_rotate</xliff:g></item> <item><xliff:g id="id">@string/status_bar_headset</xliff:g></item> <item><xliff:g id="id">@string/status_bar_data_saver</xliff:g></item> - <item><xliff:g id="id">@string/status_bar_managed_profile</xliff:g></item> <item><xliff:g id="id">@string/status_bar_ime</xliff:g></item> <item><xliff:g id="id">@string/status_bar_sync_failing</xliff:g></item> <item><xliff:g id="id">@string/status_bar_sync_active</xliff:g></item> - <item><xliff:g id="id">@string/status_bar_cast</xliff:g></item> - <item><xliff:g id="id">@string/status_bar_hotspot</xliff:g></item> - <item><xliff:g id="id">@string/status_bar_location</xliff:g></item> - <item><xliff:g id="id">@string/status_bar_bluetooth</xliff:g></item> <item><xliff:g id="id">@string/status_bar_nfc</xliff:g></item> <item><xliff:g id="id">@string/status_bar_tty</xliff:g></item> <item><xliff:g id="id">@string/status_bar_speakerphone</xliff:g></item> <item><xliff:g id="id">@string/status_bar_zen</xliff:g></item> <item><xliff:g id="id">@string/status_bar_mute</xliff:g></item> <item><xliff:g id="id">@string/status_bar_volume</xliff:g></item> + <item><xliff:g id="id">@string/status_bar_cdma_eri</xliff:g></item> + <item><xliff:g id="id">@string/status_bar_data_connection</xliff:g></item> + <item><xliff:g id="id">@string/status_bar_phone_evdo_signal</xliff:g></item> + <item><xliff:g id="id">@string/status_bar_phone_signal</xliff:g></item> + <item><xliff:g id="id">@string/status_bar_secure</xliff:g></item> + <item><xliff:g id="id">@string/status_bar_alarm_clock</xliff:g></item> + <item><xliff:g id="id">@string/status_bar_bluetooth</xliff:g></item> + <item><xliff:g id="id">@string/status_bar_managed_profile</xliff:g></item> + <item><xliff:g id="id">@string/status_bar_cast</xliff:g></item> <item><xliff:g id="id">@string/status_bar_vpn</xliff:g></item> + <item><xliff:g id="id">@string/status_bar_location</xliff:g></item> + <item><xliff:g id="id">@string/status_bar_hotspot</xliff:g></item> <item><xliff:g id="id">@string/status_bar_ethernet</xliff:g></item> <item><xliff:g id="id">@string/status_bar_wifi</xliff:g></item> <item><xliff:g id="id">@string/status_bar_mobile</xliff:g></item> <item><xliff:g id="id">@string/status_bar_airplane</xliff:g></item> - <item><xliff:g id="id">@string/status_bar_cdma_eri</xliff:g></item> - <item><xliff:g id="id">@string/status_bar_data_connection</xliff:g></item> - <item><xliff:g id="id">@string/status_bar_phone_evdo_signal</xliff:g></item> - <item><xliff:g id="id">@string/status_bar_phone_signal</xliff:g></item> <item><xliff:g id="id">@string/status_bar_battery</xliff:g></item> - <item><xliff:g id="id">@string/status_bar_alarm_clock</xliff:g></item> - <item><xliff:g id="id">@string/status_bar_secure</xliff:g></item> - <item><xliff:g id="id">@string/status_bar_clock</xliff:g></item> </string-array> <string translatable="false" name="status_bar_rotate">rotate</string> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index cb8d629abaaf..cbd7b4ddb2bc 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -2732,6 +2732,10 @@ <java-symbol type="string" name="status_bar_alarm_clock" /> <java-symbol type="string" name="status_bar_secure" /> <java-symbol type="string" name="status_bar_clock" /> + <java-symbol type="string" name="status_bar_airplane" /> + <java-symbol type="string" name="status_bar_mobile" /> + <java-symbol type="string" name="status_bar_ethernet" /> + <java-symbol type="string" name="status_bar_vpn" /> <!-- Locale picker --> <java-symbol type="id" name="locale_search_menu" /> diff --git a/core/tests/coretests/src/android/os/MemoryFileTest.java b/core/tests/coretests/src/android/os/MemoryFileTest.java index 82af662e207e..20b298d639d2 100644 --- a/core/tests/coretests/src/android/os/MemoryFileTest.java +++ b/core/tests/coretests/src/android/os/MemoryFileTest.java @@ -23,6 +23,7 @@ import android.test.suitebuilder.annotation.SmallTest; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import java.nio.BufferOverflowException; import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -110,7 +111,7 @@ public class MemoryFileTest extends AndroidTestCase { try { os.write(new byte[] { -1, -1 }); fail(); - } catch (IndexOutOfBoundsException expected) { + } catch (IndexOutOfBoundsException | BufferOverflowException expected) { } byte[] copy = new byte[file.length()]; diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java index 4b9465d24fc9..dfc99f6fcb3a 100644 --- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java +++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java @@ -267,6 +267,7 @@ public class SettingsBackupTest { Settings.Global.LOW_POWER_MODE, Settings.Global.LOW_POWER_MODE_TRIGGER_LEVEL_MAX, Settings.Global.LOW_POWER_MODE_STICKY, + Settings.Global.LOW_POWER_MODE_SUGGESTION_PARAMS, Settings.Global.LTE_SERVICE_FORCED, Settings.Global.MAX_NOTIFICATION_ENQUEUE_RATE, Settings.Global.MAX_SOUND_TRIGGER_DETECTION_SERVICE_OPS_PER_DAY, diff --git a/graphics/java/android/graphics/ImageDecoder.java b/graphics/java/android/graphics/ImageDecoder.java index 5261c0486acf..00dc22ea162b 100644 --- a/graphics/java/android/graphics/ImageDecoder.java +++ b/graphics/java/android/graphics/ImageDecoder.java @@ -855,7 +855,8 @@ public final class ImageDecoder implements AutoCloseable { */ @java.lang.Deprecated public ImageDecoder setResize(int width, int height) { - return this.setTargetSize(width, height); + this.setTargetSize(width, height); + return this; } /** @@ -873,9 +874,8 @@ public final class ImageDecoder implements AutoCloseable { * * @param width must be greater than 0. * @param height must be greater than 0. - * @return this object for chaining. */ - public ImageDecoder setTargetSize(int width, int height) { + public void setTargetSize(int width, int height) { if (width <= 0 || height <= 0) { throw new IllegalArgumentException("Dimensions must be positive! " + "provided (" + width + ", " + height + ")"); @@ -883,7 +883,6 @@ public final class ImageDecoder implements AutoCloseable { mDesiredWidth = width; mDesiredHeight = height; - return this; } /** @removed @@ -891,7 +890,8 @@ public final class ImageDecoder implements AutoCloseable { */ @java.lang.Deprecated public ImageDecoder setResize(int sampleSize) { - return this.setTargetSampleSize(sampleSize); + this.setTargetSampleSize(sampleSize); + return this; } private int getTargetDimension(int original, int sampleSize, int computed) { @@ -941,13 +941,12 @@ public final class ImageDecoder implements AutoCloseable { * {@link OnHeaderDecodedListener#onHeaderDecoded}.</p> * * @param sampleSize Sampling rate of the encoded image. - * @return this object for chaining. */ - public ImageDecoder setTargetSampleSize(int sampleSize) { + public void setTargetSampleSize(int sampleSize) { Size size = this.getSampledSize(sampleSize); int targetWidth = getTargetDimension(mWidth, sampleSize, size.getWidth()); int targetHeight = getTargetDimension(mHeight, sampleSize, size.getHeight()); - return this.setTargetSize(targetWidth, targetHeight); + this.setTargetSize(targetWidth, targetHeight); } private boolean requestedResize() { @@ -1006,14 +1005,12 @@ public final class ImageDecoder implements AutoCloseable { * {@link OnHeaderDecodedListener#onHeaderDecoded}.</p> * * @param allocator Type of allocator to use. - * @return this object for chaining. */ - public ImageDecoder setAllocator(@Allocator int allocator) { + public void setAllocator(@Allocator int allocator) { if (allocator < ALLOCATOR_DEFAULT || allocator > ALLOCATOR_HARDWARE) { throw new IllegalArgumentException("invalid allocator " + allocator); } mAllocator = allocator; - return this; } /** @@ -1039,12 +1036,9 @@ public final class ImageDecoder implements AutoCloseable { * * <p>Like all setters on ImageDecoder, this must be called inside * {@link OnHeaderDecodedListener#onHeaderDecoded}.</p> - * - * @return this object for chaining. */ - public ImageDecoder setUnpremultipliedRequired(boolean unpremultipliedRequired) { + public void setUnpremultipliedRequired(boolean unpremultipliedRequired) { mUnpremultipliedRequired = unpremultipliedRequired; - return this; } /** @removed @@ -1052,7 +1046,8 @@ public final class ImageDecoder implements AutoCloseable { */ @java.lang.Deprecated public ImageDecoder setRequireUnpremultiplied(boolean unpremultipliedRequired) { - return this.setUnpremultipliedRequired(unpremultipliedRequired); + this.setUnpremultipliedRequired(unpremultipliedRequired); + return this; } /** @@ -1086,11 +1081,9 @@ public final class ImageDecoder implements AutoCloseable { * <p>Like all setters on ImageDecoder, this must be called inside * {@link OnHeaderDecodedListener#onHeaderDecoded}.</p> * - * @return this object for chaining. */ - public ImageDecoder setPostProcessor(@Nullable PostProcessor p) { + public void setPostProcessor(@Nullable PostProcessor p) { mPostProcessor = p; - return this; } /** @@ -1110,11 +1103,9 @@ public final class ImageDecoder implements AutoCloseable { * <p>Like all setters on ImageDecoder, this must be called inside * {@link OnHeaderDecodedListener#onHeaderDecoded}.</p> * - * @return this object for chaining. */ - public ImageDecoder setOnPartialImageListener(@Nullable OnPartialImageListener l) { + public void setOnPartialImageListener(@Nullable OnPartialImageListener l) { mOnPartialImageListener = l; - return this; } /** @@ -1140,11 +1131,9 @@ public final class ImageDecoder implements AutoCloseable { * <p>Like all setters on ImageDecoder, this must be called inside * {@link OnHeaderDecodedListener#onHeaderDecoded}.</p> * - * @return this object for chaining. */ - public ImageDecoder setCrop(@Nullable Rect subset) { + public void setCrop(@Nullable Rect subset) { mCropRect = subset; - return this; } /** @@ -1164,13 +1153,10 @@ public final class ImageDecoder implements AutoCloseable { * <p>Like all setters on ImageDecoder, this must be called inside * {@link OnHeaderDecodedListener#onHeaderDecoded}.</p> * - * @return this object for chaining. - * * @hide */ - public ImageDecoder setOutPaddingRect(@NonNull Rect outPadding) { + public void setOutPaddingRect(@NonNull Rect outPadding) { mOutPaddingRect = outPadding; - return this; } /** @@ -1191,12 +1177,9 @@ public final class ImageDecoder implements AutoCloseable { * * <p>Like all setters on ImageDecoder, this must be called inside * {@link OnHeaderDecodedListener#onHeaderDecoded}.</p> - * - * @return this object for chaining. */ - public ImageDecoder setMutableRequired(boolean mutable) { + public void setMutableRequired(boolean mutable) { mMutable = mutable; - return this; } /** @removed @@ -1204,7 +1187,8 @@ public final class ImageDecoder implements AutoCloseable { */ @java.lang.Deprecated public ImageDecoder setMutable(boolean mutable) { - return this.setMutableRequired(mutable); + this.setMutableRequired(mutable); + return this; } /** @@ -1236,12 +1220,9 @@ public final class ImageDecoder implements AutoCloseable { * * <p>Like all setters on ImageDecoder, this must be called inside * {@link OnHeaderDecodedListener#onHeaderDecoded}.</p> - * - * @return this object for chaining. */ - public ImageDecoder setConserveMemory(boolean conserveMemory) { + public void setConserveMemory(boolean conserveMemory) { mConserveMemory = conserveMemory; - return this; } /** @@ -1269,12 +1250,9 @@ public final class ImageDecoder implements AutoCloseable { * * <p>Like all setters on ImageDecoder, this must be called inside * {@link OnHeaderDecodedListener#onHeaderDecoded}.</p> - * - * @return this object for chaining. */ - public ImageDecoder setDecodeAsAlphaMaskEnabled(boolean enabled) { + public void setDecodeAsAlphaMaskEnabled(boolean enabled) { mDecodeAsAlphaMask = enabled; - return this; } /** @removed @@ -1282,7 +1260,8 @@ public final class ImageDecoder implements AutoCloseable { */ @java.lang.Deprecated public ImageDecoder setDecodeAsAlphaMask(boolean enabled) { - return this.setDecodeAsAlphaMaskEnabled(enabled); + this.setDecodeAsAlphaMaskEnabled(enabled); + return this; } /** @removed @@ -1290,7 +1269,8 @@ public final class ImageDecoder implements AutoCloseable { */ @java.lang.Deprecated public ImageDecoder setAsAlphaMask(boolean asAlphaMask) { - return this.setDecodeAsAlphaMask(asAlphaMask); + this.setDecodeAsAlphaMask(asAlphaMask); + return this; } /** @@ -1350,9 +1330,8 @@ public final class ImageDecoder implements AutoCloseable { * <p>Like all setters on ImageDecoder, this must be called inside * {@link OnHeaderDecodedListener#onHeaderDecoded}.</p> */ - public ImageDecoder setTargetColorSpace(ColorSpace colorSpace) { + public void setTargetColorSpace(ColorSpace colorSpace) { mDesiredColorSpace = colorSpace; - return this; } @Override diff --git a/keystore/java/android/security/KeyStore.java b/keystore/java/android/security/KeyStore.java index 1e2ebf894f99..81644ebab692 100644 --- a/keystore/java/android/security/KeyStore.java +++ b/keystore/java/android/security/KeyStore.java @@ -16,6 +16,7 @@ package android.security; +import android.app.ActivityManager; import android.app.ActivityThread; import android.app.Application; import android.app.KeyguardManager; @@ -279,7 +280,7 @@ public class KeyStore { /** * Attempt to lock the keystore for {@code user}. * - * @param user Android user to lock. + * @param userId Android user to lock. * @return whether {@code user}'s keystore was locked. */ public boolean lock(int userId) { @@ -300,7 +301,7 @@ public class KeyStore { * This is required before keystore entries created with FLAG_ENCRYPTED can be accessed or * created. * - * @param user Android user ID to operate on + * @param userId Android user ID to operate on * @param password user's keystore password. Should be the most recent value passed to * {@link #onUserPasswordChanged} for the user. * @@ -546,6 +547,9 @@ public class KeyStore { try { args = args != null ? args : new KeymasterArguments(); entropy = entropy != null ? entropy : new byte[0]; + if (!args.containsTag(KeymasterDefs.KM_TAG_USER_ID)) { + args.addUnsignedInt(KeymasterDefs.KM_TAG_USER_ID, ActivityManager.getCurrentUser()); + } return mBinder.begin(getToken(), alias, purpose, pruneable, args, entropy, uid); } catch (RemoteException e) { Log.w(TAG, "Cannot connect to keystore", e); diff --git a/keystore/java/android/security/keystore/KeyGenParameterSpec.java b/keystore/java/android/security/keystore/KeyGenParameterSpec.java index 4b9f3c803481..c342acdf101e 100644 --- a/keystore/java/android/security/keystore/KeyGenParameterSpec.java +++ b/keystore/java/android/security/keystore/KeyGenParameterSpec.java @@ -266,6 +266,7 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAu private final boolean mInvalidatedByBiometricEnrollment; private final boolean mIsStrongBoxBacked; private final boolean mUserConfirmationRequired; + private final boolean mUnlockedDeviceRequired; /** * @hide should be built with Builder @@ -296,7 +297,8 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAu boolean userAuthenticationValidWhileOnBody, boolean invalidatedByBiometricEnrollment, boolean isStrongBoxBacked, - boolean userConfirmationRequired) { + boolean userConfirmationRequired, + boolean unlockedDeviceRequired) { if (TextUtils.isEmpty(keyStoreAlias)) { throw new IllegalArgumentException("keyStoreAlias must not be empty"); } @@ -345,6 +347,7 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAu mInvalidatedByBiometricEnrollment = invalidatedByBiometricEnrollment; mIsStrongBoxBacked = isStrongBoxBacked; mUserConfirmationRequired = userConfirmationRequired; + mUnlockedDeviceRequired = unlockedDeviceRequired; } /** @@ -670,6 +673,15 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAu } /** + * Returns {@code true} if the key cannot be used unless the device screen is unlocked. + * + * @see Builder#setUnlockedDeviceRequired(boolean) + */ + public boolean isUnlockedDeviceRequired() { + return mUnlockedDeviceRequired; + } + + /** * @hide */ public long getBoundToSpecificSecureUserId() { @@ -707,6 +719,7 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAu private boolean mInvalidatedByBiometricEnrollment = true; private boolean mIsStrongBoxBacked = false; private boolean mUserConfirmationRequired; + private boolean mUnlockedDeviceRequired = false; /** * Creates a new instance of the {@code Builder}. @@ -1275,6 +1288,18 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAu } /** + * Sets whether the keystore requires the screen to be unlocked before allowing decryption + * using this key. If this is set to {@code true}, any attempt to decrypt using this key + * while the screen is locked will fail. A locked device requires a PIN, password, + * fingerprint, or other trusted factor to access. + */ + @NonNull + public Builder setUnlockedDeviceRequired(boolean unlockedDeviceRequired) { + mUnlockedDeviceRequired = unlockedDeviceRequired; + return this; + } + + /** * Builds an instance of {@code KeyGenParameterSpec}. */ @NonNull @@ -1305,7 +1330,8 @@ public final class KeyGenParameterSpec implements AlgorithmParameterSpec, UserAu mUserAuthenticationValidWhileOnBody, mInvalidatedByBiometricEnrollment, mIsStrongBoxBacked, - mUserConfirmationRequired); + mUserConfirmationRequired, + mUnlockedDeviceRequired); } } } diff --git a/keystore/java/android/security/keystore/KeyProtection.java b/keystore/java/android/security/keystore/KeyProtection.java index 95eeec704847..22568ce7a596 100644 --- a/keystore/java/android/security/keystore/KeyProtection.java +++ b/keystore/java/android/security/keystore/KeyProtection.java @@ -224,12 +224,13 @@ public final class KeyProtection implements ProtectionParameter, UserAuthArgs { private final boolean mRandomizedEncryptionRequired; private final boolean mUserAuthenticationRequired; private final int mUserAuthenticationValidityDurationSeconds; - private final boolean mTrustedUserPresenceRequred; + private final boolean mTrustedUserPresenceRequired; private final boolean mUserAuthenticationValidWhileOnBody; private final boolean mInvalidatedByBiometricEnrollment; private final long mBoundToSecureUserId; private final boolean mCriticalToDeviceEncryption; private final boolean mUserConfirmationRequired; + private final boolean mUnlockedDeviceRequired; private KeyProtection( Date keyValidityStart, @@ -243,12 +244,13 @@ public final class KeyProtection implements ProtectionParameter, UserAuthArgs { boolean randomizedEncryptionRequired, boolean userAuthenticationRequired, int userAuthenticationValidityDurationSeconds, - boolean trustedUserPresenceRequred, + boolean trustedUserPresenceRequired, boolean userAuthenticationValidWhileOnBody, boolean invalidatedByBiometricEnrollment, long boundToSecureUserId, boolean criticalToDeviceEncryption, - boolean userConfirmationRequired) { + boolean userConfirmationRequired, + boolean unlockedDeviceRequired) { mKeyValidityStart = Utils.cloneIfNotNull(keyValidityStart); mKeyValidityForOriginationEnd = Utils.cloneIfNotNull(keyValidityForOriginationEnd); mKeyValidityForConsumptionEnd = Utils.cloneIfNotNull(keyValidityForConsumptionEnd); @@ -262,12 +264,13 @@ public final class KeyProtection implements ProtectionParameter, UserAuthArgs { mRandomizedEncryptionRequired = randomizedEncryptionRequired; mUserAuthenticationRequired = userAuthenticationRequired; mUserAuthenticationValidityDurationSeconds = userAuthenticationValidityDurationSeconds; - mTrustedUserPresenceRequred = trustedUserPresenceRequred; + mTrustedUserPresenceRequired = trustedUserPresenceRequired; mUserAuthenticationValidWhileOnBody = userAuthenticationValidWhileOnBody; mInvalidatedByBiometricEnrollment = invalidatedByBiometricEnrollment; mBoundToSecureUserId = boundToSecureUserId; mCriticalToDeviceEncryption = criticalToDeviceEncryption; mUserConfirmationRequired = userConfirmationRequired; + mUnlockedDeviceRequired = unlockedDeviceRequired; } /** @@ -444,7 +447,7 @@ public final class KeyProtection implements ProtectionParameter, UserAuthArgs { * been performed between the {@code Signature.initSign()} and {@code Signature.sign()} calls. */ public boolean isTrustedUserPresenceRequired() { - return mTrustedUserPresenceRequred; + return mTrustedUserPresenceRequired; } /** @@ -505,6 +508,15 @@ public final class KeyProtection implements ProtectionParameter, UserAuthArgs { } /** + * Returns {@code true} if the key cannot be used unless the device screen is unlocked. + * + * @see Builder#setUnlockedDeviceRequired(boolean) + */ + public boolean isUnlockedDeviceRequired() { + return mUnlockedDeviceRequired; + } + + /** * Builder of {@link KeyProtection} instances. */ public final static class Builder { @@ -524,6 +536,8 @@ public final class KeyProtection implements ProtectionParameter, UserAuthArgs { private boolean mUserAuthenticationValidWhileOnBody; private boolean mInvalidatedByBiometricEnrollment = true; private boolean mUserConfirmationRequired; + private boolean mUnlockedDeviceRequired = false; + private long mBoundToSecureUserId = GateKeeper.INVALID_SECURE_USER_ID; private boolean mCriticalToDeviceEncryption = false; @@ -914,6 +928,18 @@ public final class KeyProtection implements ProtectionParameter, UserAuthArgs { } /** + * Sets whether the keystore requires the screen to be unlocked before allowing decryption + * using this key. If this is set to {@code true}, any attempt to decrypt using this key + * while the screen is locked will fail. A locked device requires a PIN, password, + * fingerprint, or other trusted factor to access. + */ + @NonNull + public Builder setUnlockedDeviceRequired(boolean unlockedDeviceRequired) { + mUnlockedDeviceRequired = unlockedDeviceRequired; + return this; + } + + /** * Builds an instance of {@link KeyProtection}. * * @throws IllegalArgumentException if a required field is missing @@ -937,7 +963,8 @@ public final class KeyProtection implements ProtectionParameter, UserAuthArgs { mInvalidatedByBiometricEnrollment, mBoundToSecureUserId, mCriticalToDeviceEncryption, - mUserConfirmationRequired); + mUserConfirmationRequired, + mUnlockedDeviceRequired); } } } diff --git a/keystore/java/android/security/keystore/KeymasterUtils.java b/keystore/java/android/security/keystore/KeymasterUtils.java index 0ef08f237b4e..6e5012160d6e 100644 --- a/keystore/java/android/security/keystore/KeymasterUtils.java +++ b/keystore/java/android/security/keystore/KeymasterUtils.java @@ -16,9 +16,8 @@ package android.security.keystore; -import android.util.Log; +import android.app.ActivityManager; import android.hardware.fingerprint.FingerprintManager; -import android.os.UserHandle; import android.security.GateKeeper; import android.security.KeyStore; import android.security.keymaster.KeymasterArguments; @@ -101,8 +100,9 @@ public abstract class KeymasterUtils { * state (e.g., secure lock screen not set up) for generating or importing keys that * require user authentication. */ - public static void addUserAuthArgs(KeymasterArguments args, - UserAuthArgs spec) { + public static void addUserAuthArgs(KeymasterArguments args, UserAuthArgs spec) { + args.addUnsignedInt(KeymasterDefs.KM_TAG_USER_ID, ActivityManager.getCurrentUser()); + if (spec.isUserConfirmationRequired()) { args.addBoolean(KeymasterDefs.KM_TAG_TRUSTED_CONFIRMATION_REQUIRED); } @@ -111,6 +111,10 @@ public abstract class KeymasterUtils { args.addBoolean(KeymasterDefs.KM_TAG_TRUSTED_USER_PRESENCE_REQUIRED); } + if (spec.isUnlockedDeviceRequired()) { + args.addBoolean(KeymasterDefs.KM_TAG_UNLOCKED_DEVICE_REQUIRED); + } + if (!spec.isUserAuthenticationRequired()) { args.addBoolean(KeymasterDefs.KM_TAG_NO_AUTH_REQUIRED); return; diff --git a/keystore/java/android/security/keystore/UserAuthArgs.java b/keystore/java/android/security/keystore/UserAuthArgs.java index 1949592e7240..ad18ff8aef76 100644 --- a/keystore/java/android/security/keystore/UserAuthArgs.java +++ b/keystore/java/android/security/keystore/UserAuthArgs.java @@ -33,5 +33,6 @@ public interface UserAuthArgs { boolean isUserConfirmationRequired(); long getBoundToSpecificSecureUserId(); boolean isTrustedUserPresenceRequired(); + boolean isUnlockedDeviceRequired(); } diff --git a/libs/hwui/pipeline/skia/ShaderCache.cpp b/libs/hwui/pipeline/skia/ShaderCache.cpp index 2fa56f613144..670074871c71 100644 --- a/libs/hwui/pipeline/skia/ShaderCache.cpp +++ b/libs/hwui/pipeline/skia/ShaderCache.cpp @@ -83,10 +83,12 @@ sk_sp<SkData> ShaderCache::load(const SkData& key) { int maxTries = 3; while (valueSize > mObservedBlobValueSize && maxTries > 0) { mObservedBlobValueSize = std::min(valueSize, maxValueSize); - valueBuffer = realloc(valueBuffer, mObservedBlobValueSize); - if (!valueBuffer) { + void *newValueBuffer = realloc(valueBuffer, mObservedBlobValueSize); + if (!newValueBuffer) { + free(valueBuffer); return nullptr; } + valueBuffer = newValueBuffer; valueSize = bc->get(key.data(), keySize, valueBuffer, mObservedBlobValueSize); maxTries--; } diff --git a/media/java/android/media/AudioDeviceInfo.java b/media/java/android/media/AudioDeviceInfo.java index 86dfc9c88723..ca895fcdfc4a 100644 --- a/media/java/android/media/AudioDeviceInfo.java +++ b/media/java/android/media/AudioDeviceInfo.java @@ -226,11 +226,10 @@ public final class AudioDeviceInfo { } /** - * @hide * @return The "address" string of the device. This generally contains device-specific * parameters. */ - public String getAddress() { + public @NonNull String getAddress() { return mPort.address(); } diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java index e408a115787c..aeef2158b20f 100644 --- a/media/java/android/media/AudioManager.java +++ b/media/java/android/media/AudioManager.java @@ -4596,8 +4596,7 @@ public class AudioManager { private static boolean checkTypes(AudioDevicePort port) { return AudioDeviceInfo.convertInternalDeviceToDeviceType(port.type()) != - AudioDeviceInfo.TYPE_UNKNOWN && - port.type() != AudioSystem.DEVICE_IN_BACK_MIC; + AudioDeviceInfo.TYPE_UNKNOWN; } /** diff --git a/media/java/android/media/MicrophoneInfo.java b/media/java/android/media/MicrophoneInfo.java index 004efea64d2d..d6399a4163fe 100644 --- a/media/java/android/media/MicrophoneInfo.java +++ b/media/java/android/media/MicrophoneInfo.java @@ -17,6 +17,7 @@ package android.media; import android.annotation.IntDef; +import android.annotation.NonNull; import android.util.Pair; import java.lang.annotation.Retention; @@ -224,12 +225,11 @@ public final class MicrophoneInfo { } /** - * @hide * Returns The "address" string of the microphone that corresponds to the * address returned by {@link AudioDeviceInfo#getAddress()} * @return the address of the microphone */ - public String getAddress() { + public @NonNull String getAddress() { return mAddress; } diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml index 82bc6af870f0..6368607d7bdd 100644 --- a/packages/SettingsLib/res/values/strings.xml +++ b/packages/SettingsLib/res/values/strings.xml @@ -543,30 +543,30 @@ <!-- UI debug setting: Select Bluetooth AVRCP Version --> <string name="bluetooth_select_avrcp_version_dialog_title">Select Bluetooth AVRCP Version</string> - <!-- UI debug setting: Select Bluetooth Audio Codec --> + <!-- UI debug setting: Trigger Bluetooth Audio Codec Selection --> <string name="bluetooth_select_a2dp_codec_type">Bluetooth Audio Codec</string> - <!-- UI debug setting: Select Bluetooth Audio Codec --> - <string name="bluetooth_select_a2dp_codec_type_dialog_title">Select Bluetooth Audio Codec</string> + <!-- UI debug setting: Trigger Bluetooth Audio Codec Selection --> + <string name="bluetooth_select_a2dp_codec_type_dialog_title">Trigger Bluetooth Audio Codec\u000ASelection</string> - <!-- UI debug setting: Select Bluetooth Audio Sample Rate --> + <!-- UI debug setting: Trigger Bluetooth Audio Sample Rate Selection --> <string name="bluetooth_select_a2dp_codec_sample_rate">Bluetooth Audio Sample Rate</string> - <!-- UI debug setting: Select Bluetooth Audio Codec: Sample Rate --> - <string name="bluetooth_select_a2dp_codec_sample_rate_dialog_title">Select Bluetooth Audio Codec:\u000ASample Rate</string> + <!-- UI debug setting: Trigger Bluetooth Audio Codec Selection: Sample Rate --> + <string name="bluetooth_select_a2dp_codec_sample_rate_dialog_title">Trigger Bluetooth Audio Codec\u000ASelection: Sample Rate</string> - <!-- UI debug setting: Select Bluetooth Audio Bits Per Sample --> + <!-- UI debug setting: Trigger Bluetooth Audio Bits Per Sample Selection --> <string name="bluetooth_select_a2dp_codec_bits_per_sample">Bluetooth Audio Bits Per Sample</string> - <!-- UI debug setting: Select Bluetooth Audio Codec: Bits Per Sample --> - <string name="bluetooth_select_a2dp_codec_bits_per_sample_dialog_title">Select Bluetooth Audio Codec:\u000ABits Per Sample</string> + <!-- UI debug setting: Trigger Bluetooth Audio Codec Selection: Bits Per Sample --> + <string name="bluetooth_select_a2dp_codec_bits_per_sample_dialog_title">Trigger Bluetooth Audio Codec\u000ASelection: Bits Per Sample</string> - <!-- UI debug setting: Select Bluetooth Audio Channel Mode --> + <!-- UI debug setting: Trigger Bluetooth Audio Channel Mode Selection --> <string name="bluetooth_select_a2dp_codec_channel_mode">Bluetooth Audio Channel Mode</string> - <!-- UI debug setting: Select Bluetooth Audio Codec: Channel Mode --> - <string name="bluetooth_select_a2dp_codec_channel_mode_dialog_title">Select Bluetooth Audio Codec:\u000AChannel Mode</string> + <!-- UI debug setting: Trigger Bluetooth Audio Codec Selection: Channel Mode --> + <string name="bluetooth_select_a2dp_codec_channel_mode_dialog_title">Trigger Bluetooth Audio Codec\u000ASelection: Channel Mode</string> - <!-- UI debug setting: Select Bluetooth Audio LDAC Playback Quality --> + <!-- UI debug setting: Trigger Bluetooth Audio LDAC Playback Quality Selection --> <string name="bluetooth_select_a2dp_codec_ldac_playback_quality">Bluetooth Audio LDAC Codec: Playback Quality</string> <!-- UI debug setting: Select Bluetooth Audio LDAC Codec: LDAC Playback Quality --> - <string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title">Select Bluetooth Audio LDAC Codec:\u000APlayback Quality</string> + <string name="bluetooth_select_a2dp_codec_ldac_playback_quality_dialog_title">Trigger Bluetooth Audio LDAC Codec\u000ASelection: Playback Quality</string> <!-- [CHAR LIMIT=NONE] Label for displaying Bluetooth Audio Codec Parameters while streaming --> <string name="bluetooth_select_a2dp_codec_streaming_label">Streaming: <xliff:g id="streaming_parameter">%1$s</xliff:g></string> diff --git a/packages/SettingsLib/src/com/android/settingslib/fuelgauge/BatterySaverUtils.java b/packages/SettingsLib/src/com/android/settingslib/fuelgauge/BatterySaverUtils.java index 28833a349372..835ff07c4006 100644 --- a/packages/SettingsLib/src/com/android/settingslib/fuelgauge/BatterySaverUtils.java +++ b/packages/SettingsLib/src/com/android/settingslib/fuelgauge/BatterySaverUtils.java @@ -22,8 +22,9 @@ import android.content.Intent; import android.os.PowerManager; import android.provider.Settings.Global; import android.provider.Settings.Secure; -import android.support.annotation.VisibleForTesting; +import android.util.KeyValueListParser; import android.util.Log; +import android.util.Slog; /** * Utilities related to battery saver. @@ -48,13 +49,35 @@ public class BatterySaverUtils { public static final String ACTION_SHOW_AUTO_SAVER_SUGGESTION = "PNW.autoSaverSuggestion"; - /** - * We show the auto battery saver suggestion notification when the user manually enables - * battery saver for the START_NTH time through the END_NTH time. - * (We won't show it for END_NTH + 1 time and after.) - */ - private static final int AUTO_SAVER_SUGGESTION_START_NTH = 4; - private static final int AUTO_SAVER_SUGGESTION_END_NTH = 8; + private static class Parameters { + private final Context mContext; + + /** + * We show the auto battery saver suggestion notification when the user manually enables + * battery saver for the START_NTH time through the END_NTH time. + * (We won't show it for END_NTH + 1 time and after.) + */ + private static final int AUTO_SAVER_SUGGESTION_START_NTH = 4; + private static final int AUTO_SAVER_SUGGESTION_END_NTH = 8; + + public final int startNth; + public final int endNth; + + public Parameters(Context context) { + mContext = context; + + final String newValue = Global.getString(mContext.getContentResolver(), + Global.LOW_POWER_MODE_SUGGESTION_PARAMS); + final KeyValueListParser parser = new KeyValueListParser(','); + try { + parser.setString(newValue); + } catch (IllegalArgumentException e) { + Slog.wtf(TAG, "Bad constants: " + newValue); + } + startNth = parser.getInt("start_nth", AUTO_SAVER_SUGGESTION_START_NTH); + endNth = parser.getInt("end_nth", AUTO_SAVER_SUGGESTION_END_NTH); + } + } /** * Enable / disable battery saver by user request. @@ -85,8 +108,10 @@ public class BatterySaverUtils { Secure.getInt(cr, Secure.LOW_POWER_MANUAL_ACTIVATION_COUNT, 0) + 1; Secure.putInt(cr, Secure.LOW_POWER_MANUAL_ACTIVATION_COUNT, count); - if ((count >= AUTO_SAVER_SUGGESTION_START_NTH) - && (count <= AUTO_SAVER_SUGGESTION_END_NTH) + final Parameters parameters = new Parameters(context); + + if ((count >= parameters.startNth) + && (count <= parameters.endNth) && Global.getInt(cr, Global.LOW_POWER_MODE_TRIGGER_LEVEL, 0) == 0 && Secure.getInt(cr, Secure.SUPPRESS_AUTO_BATTERY_SAVER_SUGGESTION, 0) == 0) { diff --git a/packages/SystemUI/res/layout/quick_qs_status_icons.xml b/packages/SystemUI/res/layout/quick_qs_status_icons.xml index cd3271fa5374..c03f25c75101 100644 --- a/packages/SystemUI/res/layout/quick_qs_status_icons.xml +++ b/packages/SystemUI/res/layout/quick_qs_status_icons.xml @@ -21,7 +21,7 @@ android:layout_marginTop="8dp" android:layout_marginBottom="14dp" android:layout_below="@id/quick_status_bar_system_icons" - > + android:paddingEnd="@dimen/signal_cluster_battery_padding" > <com.android.systemui.statusbar.phone.StatusIconContainer android:id="@+id/statusIcons" @@ -29,9 +29,4 @@ android:layout_height="match_parent" android:layout_weight="1" /> - <include layout="@layout/signal_cluster_view" - android:layout_width="wrap_content" - android:layout_height="match_parent" - android:layout_marginStart="@dimen/signal_cluster_margin_start" /> - </LinearLayout> diff --git a/packages/SystemUI/res/layout/status_bar_mobile_signal_group.xml b/packages/SystemUI/res/layout/status_bar_mobile_signal_group.xml new file mode 100644 index 000000000000..d607c8c29ead --- /dev/null +++ b/packages/SystemUI/res/layout/status_bar_mobile_signal_group.xml @@ -0,0 +1,81 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +** +** Copyright 2018, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> +<com.android.systemui.statusbar.StatusBarMobileView + xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:systemui="http://schemas.android.com/apk/res-auto" + android:id="@+id/mobile_combo" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="center_vertical" + android:orientation="horizontal"> + <FrameLayout + android:id="@+id/inout_container" + android:layout_height="17dp" + android:layout_width="wrap_content" + android:layout_gravity="center_vertical"> + <ImageView + android:id="@+id/mobile_in" + android:layout_height="wrap_content" + android:layout_width="wrap_content" + android:src="@drawable/ic_activity_down" + android:visibility="gone" + android:paddingEnd="2dp" + /> + <ImageView + android:id="@+id/mobile_out" + android:layout_height="wrap_content" + android:layout_width="wrap_content" + android:src="@drawable/ic_activity_up" + android:paddingEnd="2dp" + android:visibility="gone" + /> + </FrameLayout> + <ImageView + android:id="@+id/mobile_type" + android:layout_height="wrap_content" + android:layout_width="wrap_content" + android:layout_gravity="center_vertical" + android:paddingEnd="1dp" + android:visibility="gone" /> + <Space + android:id="@+id/mobile_roaming_space" + android:layout_height="match_parent" + android:layout_width="@dimen/roaming_icon_start_padding" + android:visibility="gone" + /> + <FrameLayout + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="center_vertical"> + <com.android.systemui.statusbar.AnimatedImageView + android:id="@+id/mobile_signal" + android:layout_height="wrap_content" + android:layout_width="wrap_content" + systemui:hasOverlappingRendering="false" + /> + <ImageView + android:id="@+id/mobile_roaming" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:src="@drawable/stat_sys_roaming" + android:contentDescription="@string/data_connection_roaming" + android:visibility="gone" /> + </FrameLayout> +</com.android.systemui.statusbar.StatusBarMobileView> + diff --git a/packages/SystemUI/res/layout/status_bar_wifi_group.xml b/packages/SystemUI/res/layout/status_bar_wifi_group.xml new file mode 100644 index 000000000000..08cef55e0393 --- /dev/null +++ b/packages/SystemUI/res/layout/status_bar_wifi_group.xml @@ -0,0 +1,82 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +** +** Copyright 2018, The Android Open Source Project +** +** Licensed under the Apache License, Version 2.0 (the "License"); +** you may not use this file except in compliance with the License. +** You may obtain a copy of the License at +** +** http://www.apache.org/licenses/LICENSE-2.0 +** +** Unless required by applicable law or agreed to in writing, software +** distributed under the License is distributed on an "AS IS" BASIS, +** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +** See the License for the specific language governing permissions and +** limitations under the License. +*/ +--> +<com.android.systemui.statusbar.StatusBarWifiView + xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:systemui="http://schemas.android.com/apk/res-auto" + android:id="@+id/wifi_combo" + android:layout_width="wrap_content" + android:layout_height="match_parent" + android:paddingStart="4dp" + android:gravity="center_vertical" + android:orientation="horizontal" > + + <FrameLayout + android:id="@+id/inout_container" + android:layout_height="17dp" + android:layout_width="wrap_content" + android:gravity="center_vertical" > + <ImageView + android:id="@+id/wifi_in" + android:layout_height="wrap_content" + android:layout_width="wrap_content" + android:src="@drawable/ic_activity_down" + android:visibility="gone" + android:paddingEnd="2dp" + /> + <ImageView + android:id="@+id/wifi_out" + android:layout_height="wrap_content" + android:layout_width="wrap_content" + android:src="@drawable/ic_activity_up" + android:paddingEnd="2dp" + android:visibility="gone" + /> + </FrameLayout> + <FrameLayout + android:id="@+id/wifi_combo" + android:layout_height="wrap_content" + android:layout_width="wrap_content" + android:gravity="center_vertical" > + <com.android.systemui.statusbar.AlphaOptimizedImageView + android:theme="?attr/lightIconTheme" + android:id="@+id/wifi_signal" + android:layout_height="wrap_content" + android:layout_width="wrap_content" /> + </FrameLayout> + + <View + android:id="@+id/wifi_signal_spacer" + android:layout_width="@dimen/status_bar_wifi_signal_spacer_width" + android:layout_height="4dp" + android:visibility="gone" /> + + <!-- Looks like CarStatusBar uses this... --> + <ViewStub + android:id="@+id/connected_device_signals_stub" + android:layout="@layout/connected_device_signal" + android:layout_width="wrap_content" + android:layout_height="wrap_content" /> + + <View + android:id="@+id/wifi_airplane_spacer" + android:layout_width="@dimen/status_bar_airplane_spacer_width" + android:layout_height="4dp" + android:visibility="gone" + /> +</com.android.systemui.statusbar.StatusBarWifiView> diff --git a/packages/SystemUI/res/layout/system_icons.xml b/packages/SystemUI/res/layout/system_icons.xml index 1fafb2fc72d6..258b82aacb2e 100644 --- a/packages/SystemUI/res/layout/system_icons.xml +++ b/packages/SystemUI/res/layout/system_icons.xml @@ -24,14 +24,10 @@ android:layout_width="0dp" android:layout_weight="1" android:layout_height="match_parent" + android:paddingEnd="4dp" android:gravity="center_vertical" android:orientation="horizontal"/> - <include layout="@layout/signal_cluster_view" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_marginStart="@dimen/signal_cluster_margin_start"/> - <com.android.systemui.BatteryMeterView android:id="@+id/battery" android:layout_height="match_parent" android:layout_width="wrap_content" diff --git a/packages/SystemUI/res/values/dimens_car.xml b/packages/SystemUI/res/values/dimens_car.xml index 5679dd2f1fb0..6caed61f11a7 100644 --- a/packages/SystemUI/res/values/dimens_car.xml +++ b/packages/SystemUI/res/values/dimens_car.xml @@ -32,6 +32,8 @@ <dimen name="car_navigation_button_width">64dp</dimen> <dimen name="car_navigation_bar_width">760dp</dimen> + <dimen name="car_left_navigation_bar_width">96dp</dimen> + <dimen name="car_right_navigation_bar_width">96dp</dimen> <dimen name="car_page_indicator_dot_diameter">12dp</dimen> <dimen name="car_page_indicator_margin_bottom">24dp</dimen> diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index e9b2be9b5b3e..4074042d3615 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -1748,6 +1748,10 @@ <!-- Name of the headset in status bar [CHAR LIMIT=30] --> <string name="headset">Headset</string> + <!-- Accessibility description for long click on a quick settings tile - this is used in the + context of the sentence "double tap and hold to _Open settings_" [CHAR LIMIT=NONE] --> + <string name="accessibility_long_click_tile">Open settings</string> + <!-- Accessibility description of headphones icon [CHAR LIMIT=NONE] --> <string name="accessibility_status_bar_headphones">Headphones connected</string> diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java index 2c0e95b5af26..a61ce8c291fd 100644 --- a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java +++ b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java @@ -29,30 +29,16 @@ import android.os.SystemProperties; import android.os.Trace; import android.os.UserHandle; import android.util.ArraySet; -import android.util.TimingsTraceLog; import android.util.Log; +import android.util.TimingsTraceLog; -import com.android.systemui.globalactions.GlobalActionsComponent; -import com.android.systemui.keyboard.KeyboardUI; -import com.android.systemui.keyguard.KeyguardViewMediator; -import com.android.systemui.media.RingtonePlayer; -import com.android.systemui.pip.PipUI; import com.android.systemui.plugins.OverlayPlugin; import com.android.systemui.plugins.PluginListener; import com.android.systemui.plugins.PluginManager; -import com.android.systemui.power.PowerUI; -import com.android.systemui.recents.Recents; -import com.android.systemui.shortcut.ShortcutKeyDispatcher; -import com.android.systemui.stackdivider.Divider; -import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.phone.StatusBar; import com.android.systemui.statusbar.phone.StatusBarWindowManager; -import com.android.systemui.usb.StorageNotification; import com.android.systemui.util.NotificationChannels; -import com.android.systemui.util.leak.GarbageMonitor; -import com.android.systemui.volume.VolumeUI; -import java.util.ArrayList; import java.util.HashMap; import java.util.Map; @@ -99,6 +85,10 @@ public class SystemUIApplication extends Application implements SysUiServiceProv mServices[i].onBootCompleted(); } } + + IntentFilter localeChangedFilter = new IntentFilter( + Intent.ACTION_LOCALE_CHANGED); + registerReceiver(mLocaleChangeReceiver, localeChangedFilter); } }, filter); } else { @@ -249,4 +239,14 @@ public class SystemUIApplication extends Application implements SysUiServiceProv public SystemUI[] getServices() { return mServices; } + + private final BroadcastReceiver mLocaleChangeReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + if (Intent.ACTION_LOCALE_CHANGED.equals(intent.getAction())) { + // Update names of SystemUi notification channels + NotificationChannels.createAll(context); + } + } + }; } diff --git a/packages/SystemUI/src/com/android/systemui/power/PowerUI.java b/packages/SystemUI/src/com/android/systemui/power/PowerUI.java index f08219a8f742..c409f738ec2a 100644 --- a/packages/SystemUI/src/com/android/systemui/power/PowerUI.java +++ b/packages/SystemUI/src/com/android/systemui/power/PowerUI.java @@ -52,6 +52,7 @@ import com.android.systemui.statusbar.phone.StatusBar; import java.io.FileDescriptor; import java.io.PrintWriter; +import java.time.Duration; import java.util.Arrays; public class PowerUI extends SystemUI { @@ -61,6 +62,8 @@ public class PowerUI extends SystemUI { private static final long TEMPERATURE_LOGGING_INTERVAL = DateUtils.HOUR_IN_MILLIS; private static final int MAX_RECENT_TEMPS = 125; // TEMPERATURE_LOGGING_INTERVAL plus a buffer static final long THREE_HOURS_IN_MILLIS = DateUtils.HOUR_IN_MILLIS * 3; + private static final int CHARGE_CYCLE_PERCENT_RESET = 45; + private static final long SIX_HOURS_MILLIS = Duration.ofHours(6).toMillis(); private final Handler mHandler = new Handler(); private final Receiver mReceiver = new Receiver(); @@ -69,7 +72,6 @@ public class PowerUI extends SystemUI { private HardwarePropertiesManager mHardwarePropertiesManager; private WarningsUI mWarnings; private final Configuration mLastConfiguration = new Configuration(); - private int mBatteryLevel = 100; private long mTimeRemaining = Long.MAX_VALUE; private int mPlugType = 0; private int mInvalidCharger = 0; @@ -88,6 +90,7 @@ public class PowerUI extends SystemUI { private long mNextLogTime; private IThermalService mThermalService; + @VisibleForTesting int mBatteryLevel = 100; @VisibleForTesting int mBatteryStatus = BatteryManager.BATTERY_STATUS_UNKNOWN; // by using the same instance (method references are not guaranteed to be the same object @@ -205,12 +208,6 @@ public class PowerUI extends SystemUI { final boolean plugged = mPlugType != 0; final boolean oldPlugged = oldPlugType != 0; - // if we are now unplugged but we were previously plugged in we should allow the - // time based trigger again. - if (!plugged && plugged != oldPlugged) { - mLowWarningShownThisChargeCycle = false; - mSevereWarningShownThisChargeCycle = false; - } int oldBucket = findBatteryLevelBucket(oldBatteryLevel); int bucket = findBatteryLevelBucket(mBatteryLevel); @@ -261,7 +258,8 @@ public class PowerUI extends SystemUI { boolean isPowerSaver = mPowerManager.isPowerSaveMode(); // only play SFX when the dialog comes up or the bucket changes final boolean playSound = bucket != oldBucket || oldPlugged; - if (mEnhancedEstimates.isHybridNotificationEnabled()) { + final boolean hybridEnabled = mEnhancedEstimates.isHybridNotificationEnabled(); + if (hybridEnabled) { final Estimate estimate = mEnhancedEstimates.getEstimate(); // Turbo is not always booted once SysUI is running so we have ot make sure we actually // get data back @@ -270,6 +268,14 @@ public class PowerUI extends SystemUI { mWarnings.updateEstimate(estimate); mWarnings.updateThresholds(mEnhancedEstimates.getLowWarningThreshold(), mEnhancedEstimates.getSevereWarningThreshold()); + + // if we are now over 45% battery & 6 hours remaining we can trigger hybrid + // notification again + if (mBatteryLevel >= CHARGE_CYCLE_PERCENT_RESET + && mTimeRemaining > SIX_HOURS_MILLIS) { + mLowWarningShownThisChargeCycle = false; + mSevereWarningShownThisChargeCycle = false; + } } } @@ -277,13 +283,15 @@ public class PowerUI extends SystemUI { mTimeRemaining, isPowerSaver, mBatteryStatus)) { mWarnings.showLowBatteryWarning(playSound); - // mark if we've already shown a warning this cycle. This will prevent the time based - // trigger from spamming users since the time remaining can vary based on current - // device usage. - if (mTimeRemaining < mEnhancedEstimates.getSevereWarningThreshold()) { - mSevereWarningShownThisChargeCycle = true; - } else { - mLowWarningShownThisChargeCycle = true; + // mark if we've already shown a warning this cycle. This will prevent the notification + // trigger from spamming users by only showing low/critical warnings once per cycle + if (hybridEnabled) { + if (mTimeRemaining < mEnhancedEstimates.getSevereWarningThreshold() + || mBatteryLevel < mLowBatteryReminderLevels[1]) { + mSevereWarningShownThisChargeCycle = true; + } else { + mLowWarningShownThisChargeCycle = true; + } } } else if (shouldDismissLowBatteryWarning(plugged, oldBucket, bucket, mTimeRemaining, isPowerSaver)) { @@ -295,12 +303,16 @@ public class PowerUI extends SystemUI { @VisibleForTesting boolean shouldShowLowBatteryWarning(boolean plugged, boolean oldPlugged, int oldBucket, - int bucket, long timeRemaining, boolean isPowerSaver, int mBatteryStatus) { + int bucket, long timeRemaining, boolean isPowerSaver, int batteryStatus) { + if (mEnhancedEstimates.isHybridNotificationEnabled()) { + // triggering logic when enhanced estimate is available + return isEnhancedTrigger(plugged, timeRemaining, isPowerSaver, batteryStatus); + } + // legacy triggering logic return !plugged && !isPowerSaver - && (((bucket < oldBucket || oldPlugged) && bucket < 0) - || isTimeBasedTrigger(timeRemaining)) - && mBatteryStatus != BatteryManager.BATTERY_STATUS_UNKNOWN; + && (((bucket < oldBucket || oldPlugged) && bucket < 0)) + && batteryStatus != BatteryManager.BATTERY_STATUS_UNKNOWN; } @VisibleForTesting @@ -315,19 +327,23 @@ public class PowerUI extends SystemUI { || hybridWouldDismiss)); } - private boolean isTimeBasedTrigger(long timeRemaining) { - if (!mEnhancedEstimates.isHybridNotificationEnabled()) { + private boolean isEnhancedTrigger(boolean plugged, long timeRemaining, boolean isPowerSaver, + int batteryStatus) { + if (plugged || isPowerSaver || batteryStatus == BatteryManager.BATTERY_STATUS_UNKNOWN) { return false; } - - // Only show the time based warning once per charge cycle - final boolean canShowWarning = timeRemaining < mEnhancedEstimates.getLowWarningThreshold() - && !mLowWarningShownThisChargeCycle; - - // Only show the severe time based warning once per charge cycle - final boolean canShowSevereWarning = - timeRemaining < mEnhancedEstimates.getSevereWarningThreshold() - && !mSevereWarningShownThisChargeCycle; + int warnLevel = mLowBatteryReminderLevels[0]; + int critLevel = mLowBatteryReminderLevels[1]; + + // Only show the low warning once per charge cycle + final boolean canShowWarning = !mLowWarningShownThisChargeCycle + && (timeRemaining < mEnhancedEstimates.getLowWarningThreshold() + || mBatteryLevel <= warnLevel); + + // Only show the severe warning once per charge cycle + final boolean canShowSevereWarning = !mSevereWarningShownThisChargeCycle + && (timeRemaining < mEnhancedEstimates.getSevereWarningThreshold() + || mBatteryLevel <= critLevel); return canShowWarning || canShowSevereWarning; } diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java index 0ac8b9ccef7e..df65d1fb82cd 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java @@ -47,7 +47,6 @@ import com.android.systemui.BatteryMeterView; import com.android.systemui.Dependency; import com.android.systemui.Prefs; import com.android.systemui.R; -import com.android.systemui.R.id; import com.android.systemui.SysUiServiceProvider; import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.qs.QSDetail.Callback; @@ -161,7 +160,6 @@ public class QuickStatusBarHeader extends RelativeLayout implements CommandQueue // Set light text on the header icons because they will always be on a black background applyDarkness(R.id.clock, tintArea, 0, DarkIconDispatcher.DEFAULT_ICON_TINT); - applyDarkness(id.signal_cluster, tintArea, intensity, colorForeground); // Set the correct tint for the status icons so they contrast mIconManager.setTint(fillColor); diff --git a/packages/SystemUI/src/com/android/systemui/qs/car/CarStatusBarHeader.java b/packages/SystemUI/src/com/android/systemui/qs/car/CarStatusBarHeader.java index ec183769c763..31c455d880d6 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/car/CarStatusBarHeader.java +++ b/packages/SystemUI/src/com/android/systemui/qs/car/CarStatusBarHeader.java @@ -44,7 +44,6 @@ public class CarStatusBarHeader extends LinearLayout { float intensity = colorForeground == Color.WHITE ? 0f : 1f; Rect tintArea = new Rect(0, 0, 0, 0); - applyDarkness(R.id.signal_cluster, tintArea, intensity, colorForeground); applyDarkness(R.id.battery, tintArea, intensity, colorForeground); applyDarkness(R.id.clock, tintArea, intensity, colorForeground); diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/TileServices.java b/packages/SystemUI/src/com/android/systemui/qs/external/TileServices.java index 943a17644b55..6f664d7debc0 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/external/TileServices.java +++ b/packages/SystemUI/src/com/android/systemui/qs/external/TileServices.java @@ -102,7 +102,9 @@ public class TileServices extends IQSService.Stub { mTokenMap.remove(service.getToken()); mTiles.remove(tile.getComponent()); final String slot = tile.getComponent().getClassName(); - mMainHandler.post(() -> mHost.getIconController().removeIcon(slot)); + // TileServices doesn't know how to add more than 1 icon per slot, so remove all + mMainHandler.post(() -> mHost.getIconController() + .removeAllIconsForSlot(slot)); } } diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java index a9defc8b75a0..09d928fd3657 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileBaseView.java @@ -194,6 +194,7 @@ public class QSTileBaseView extends com.android.systemui.plugins.qs.QSTileView { setClickable(state.state != Tile.STATE_UNAVAILABLE); mIcon.setIcon(state); setContentDescription(state.contentDescription); + mAccessibilityClass = state.expandedAccessibilityClassName; if (state instanceof QSTile.BooleanState) { boolean newState = ((BooleanState) state).value; @@ -269,6 +270,10 @@ public class QSTileBaseView extends com.android.systemui.plugins.qs.QSTileView { info.setText(label); info.setChecked(b); info.setCheckable(true); + info.addAction( + new AccessibilityNodeInfo.AccessibilityAction( + AccessibilityNodeInfo.AccessibilityAction.ACTION_LONG_CLICK.getId(), + getResources().getString(R.string.accessibility_long_click_tile))); } } } diff --git a/packages/SystemUI/src/com/android/systemui/recents/Recents.java b/packages/SystemUI/src/com/android/systemui/recents/Recents.java index 674ccd8c3471..0f85c5b37f65 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/Recents.java +++ b/packages/SystemUI/src/com/android/systemui/recents/Recents.java @@ -492,6 +492,13 @@ public class Recents extends SystemUI } } mDraggingInRecentsCurrentUser = currentUser; + + if (mOverviewProxyService.getProxy() != null) { + // The overview service is handling split screen, so just skip the wait for the + // first draw and notify the divider to start animating now + EventBus.getDefault().post(new RecentsDrawnEvent()); + } + return true; } else { EventBus.getDefault().send(new ShowUserToastEvent( diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java index 03b263d2ae95..3ece2f958100 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java @@ -16,7 +16,6 @@ package com.android.systemui.statusbar; -import static android.service.notification.NotificationListenerService.Ranking.USER_SENTIMENT_NEGATIVE; import static com.android.systemui.statusbar.notification.ActivityLaunchAnimator.ExpandAnimationParameters; import static com.android.systemui.statusbar.notification.NotificationInflater.InflationCallback; @@ -25,7 +24,10 @@ import android.animation.AnimatorListenerAdapter; import android.animation.ObjectAnimator; import android.animation.ValueAnimator.AnimatorUpdateListener; import android.annotation.Nullable; +import android.app.NotificationChannel; import android.content.Context; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; import android.content.res.Configuration; import android.content.res.Resources; import android.graphics.Path; @@ -39,6 +41,7 @@ import android.service.notification.StatusBarNotification; import android.util.ArraySet; import android.util.AttributeSet; import android.util.FloatProperty; +import android.util.Log; import android.util.MathUtils; import android.util.Property; import android.view.KeyEvent; @@ -90,12 +93,17 @@ import java.util.List; import java.util.function.BooleanSupplier; import java.util.function.Consumer; +/** + * View representing a notification item - this can be either the individual child notification or + * the group summary (which contains 1 or more child notifications). + */ public class ExpandableNotificationRow extends ActivatableNotificationView implements PluginListener<NotificationMenuRowPlugin> { private static final int DEFAULT_DIVIDER_ALPHA = 0x29; private static final int COLORED_DIVIDER_ALPHA = 0x7B; private static final int MENU_VIEW_INDEX = 0; + private static final String TAG = "ExpandableNotifRow"; public interface LayoutListener { public void onLayout(); @@ -166,6 +174,8 @@ public class ExpandableNotificationRow extends ActivatableNotificationView private NotificationGuts mGuts; private NotificationData.Entry mEntry; private StatusBarNotification mStatusBarNotification; + private PackageManager mCachedPackageManager; + private PackageInfo mCachedPackageInfo; private String mAppName; private boolean mIsHeadsUp; private boolean mLastChronometerRunning = true; @@ -372,6 +382,53 @@ public class ExpandableNotificationRow extends ActivatableNotificationView mEntry = entry; mStatusBarNotification = entry.notification; mNotificationInflater.inflateNotificationViews(); + + perhapsCachePackageInfo(); + } + + /** + * Caches the package manager and info objects which are expensive to obtain. + */ + private void perhapsCachePackageInfo() { + if (mCachedPackageInfo == null) { + mCachedPackageManager = StatusBar.getPackageManagerForUser( + mContext, mStatusBarNotification.getUser().getIdentifier()); + try { + mCachedPackageInfo = mCachedPackageManager.getPackageInfo( + mStatusBarNotification.getPackageName(), PackageManager.GET_SIGNATURES); + } catch (PackageManager.NameNotFoundException e) { + Log.e(TAG, "perhapsCachePackageInfo: Could not find package info"); + } + } + } + + /** + * Returns whether this row is considered non-blockable (e.g. it's a non-blockable system notif, + * covers multiple channels, or is in a whitelist). + */ + public boolean getIsNonblockable() { + boolean isNonblockable; + + isNonblockable = Dependency.get(NotificationBlockingHelperManager.class) + .isNonblockablePackage(mStatusBarNotification.getPackageName()); + + // Only bother with going through the children if the row is still blockable based on the + // number of unique channels. + if (!isNonblockable) { + isNonblockable = getNumUniqueChannels() > 1; + } + + // Only bother with IPC if the package is still blockable. + if (!isNonblockable && mCachedPackageManager != null && mCachedPackageInfo != null) { + if (com.android.settingslib.Utils.isSystemPackage( + mContext.getResources(), mCachedPackageManager, mCachedPackageInfo)) { + if (mEntry.channel != null + && !mEntry.channel.isBlockableSystem()) { + isNonblockable = true; + } + } + } + return isNonblockable; } public void onNotificationUpdated() { @@ -2019,6 +2076,32 @@ public class ExpandableNotificationRow extends ActivatableNotificationView updateChildrenVisibility(); applyChildrenRoundness(); } + /** + * Returns the number of channels covered by the notification row (including its children if + * it's a summary notification). + */ + public int getNumUniqueChannels() { + ArraySet<NotificationChannel> channels = new ArraySet<>(); + + channels.add(mEntry.channel); + + // If this is a summary, then add in the children notification channels for the + // same user and pkg. + if (mIsSummaryWithChildren) { + final List<ExpandableNotificationRow> childrenRows = getNotificationChildren(); + final int numChildren = childrenRows.size(); + for (int i = 0; i < numChildren; i++) { + final ExpandableNotificationRow childRow = childrenRows.get(i); + final NotificationChannel childChannel = childRow.getEntry().channel; + final StatusBarNotification childSbn = childRow.getStatusBarNotification(); + if (childSbn.getUser().equals(mStatusBarNotification.getUser()) && + childSbn.getPackageName().equals(mStatusBarNotification.getPackageName())) { + channels.add(childChannel); + } + } + } + return channels.size(); + } public void updateChildrenHeaderAppearance() { if (mIsSummaryWithChildren) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NeutralGoodDrawable.java b/packages/SystemUI/src/com/android/systemui/statusbar/NeutralGoodDrawable.java new file mode 100644 index 000000000000..cdb0514a2686 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NeutralGoodDrawable.java @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar; + +import android.content.Context; +import android.content.res.Resources.Theme; +import android.graphics.Canvas; +import android.graphics.ColorFilter; +import android.graphics.Rect; +import android.graphics.drawable.Drawable; +import android.graphics.drawable.LayerDrawable; +import android.view.ContextThemeWrapper; +import android.view.Gravity; +import com.android.settingslib.Utils; +import com.android.systemui.R; + +/** + * NeutralGoodDrawable implements a drawable that will load 2 underlying drawable resources, one + * with each the DualToneDarkTheme and DualToneLightTheme, choosing which one based on what + * DarkIconDispatcher tells us about darkness + */ +public class NeutralGoodDrawable extends LayerDrawable { + + public static NeutralGoodDrawable create(Context context, int resId) { + int dualToneLightTheme = Utils.getThemeAttr(context, R.attr.lightIconTheme); + int dualToneDarkTheme = Utils.getThemeAttr(context, R.attr.darkIconTheme); + ContextThemeWrapper light = new ContextThemeWrapper(context, dualToneLightTheme); + ContextThemeWrapper dark = new ContextThemeWrapper(context, dualToneDarkTheme); + + return create(light, dark, resId); + } + + /** + * For the on-the-go young entrepreneurial who wants to cache contexts + * @param light - a context using the R.attr.lightIconTheme + * @param dark - a context using the R.attr.darkIconTheme + * @param resId - the resId for our drawable + */ + public static NeutralGoodDrawable create(Context light, Context dark, int resId) { + return new NeutralGoodDrawable( + new Drawable[] { + light.getDrawable(resId).mutate(), + dark.getDrawable(resId).mutate() }); + } + + protected NeutralGoodDrawable(Drawable []drawables) { + super(drawables); + + for (int i = 0; i < drawables.length; i++) { + setLayerGravity(i, Gravity.CENTER); + } + + mutate(); + setDarkIntensity(0); + } + + public void setDarkIntensity(float intensity) { + + getDrawable(0).setAlpha((int) ((1 - intensity) * 255f)); + getDrawable(1).setAlpha((int) (intensity * 255f)); + + invalidateSelf(); + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationBlockingHelperManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationBlockingHelperManager.java index c9c1bc6fdc39..20e5f86ee097 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationBlockingHelperManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationBlockingHelperManager.java @@ -17,11 +17,19 @@ package com.android.systemui.statusbar; import android.content.Context; +import android.content.pm.PackageManager; +import android.os.UserHandle; +import android.service.notification.StatusBarNotification; import android.support.annotation.VisibleForTesting; import android.util.Log; import com.android.systemui.Dependency; import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin; +import com.android.systemui.statusbar.phone.StatusBar; + +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; import static android.service.notification.NotificationListenerService.Ranking.USER_SENTIMENT_NEGATIVE; @@ -37,6 +45,7 @@ public class NotificationBlockingHelperManager { private final Context mContext; /** Row that the blocking helper will be shown in (via {@link NotificationGuts}. */ private ExpandableNotificationRow mBlockingHelperRow; + private Set<String> mNonBlockablePkgs; /** * Whether the notification shade/stack is expanded - used to determine blocking helper @@ -46,6 +55,9 @@ public class NotificationBlockingHelperManager { public NotificationBlockingHelperManager(Context context) { mContext = context; + mNonBlockablePkgs = new HashSet<>(); + Collections.addAll(mNonBlockablePkgs, mContext.getResources().getStringArray( + com.android.internal.R.array.config_nonBlockableNotificationPackages)); } /** @@ -59,15 +71,14 @@ public class NotificationBlockingHelperManager { */ boolean perhapsShowBlockingHelper( ExpandableNotificationRow row, NotificationMenuRowPlugin menuRow) { - int numChildren = row.getNumberOfNotificationChildren(); - // We only show the blocking helper if: - // - The dismissed row is a valid group (>1 or 0 children) or the only child in the group + // - User sentiment is negative (DEBUG flag can bypass) // - The notification shade is fully expanded (guarantees we're not touching a HUN). - // - User sentiment is negative - if (DEBUG - || row.getEntry().userSentiment == USER_SENTIMENT_NEGATIVE + // - The row is blockable (i.e. not non-blockable) + // - The dismissed row is a valid group (>1 or 0 children) or the only child in the group + if ((row.getEntry().userSentiment == USER_SENTIMENT_NEGATIVE || DEBUG) && mIsShadeExpanded + && !row.getIsNonblockable() && (!row.isChildInGroup() || row.isOnlyChildInGroup())) { // Dismiss any current blocking helper before continuing forward (only one can be shown // at a given time). @@ -125,6 +136,13 @@ public class NotificationBlockingHelperManager { mIsShadeExpanded = expandedHeight > 0.0f; } + /** + * Returns whether the given package name is in the list of non-blockable packages. + */ + public boolean isNonblockablePackage(String packageName) { + return mNonBlockablePkgs.contains(packageName); + } + @VisibleForTesting boolean isBlockingHelperRowNull() { return mBlockingHelperRow == null; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGutsManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGutsManager.java index 75204d9513fe..dff5f3814f68 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGutsManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGutsManager.java @@ -65,7 +65,6 @@ public class NotificationGutsManager implements Dumpable { private static final String EXTRA_FRAGMENT_ARG_KEY = ":settings:fragment_args_key"; private final MetricsLogger mMetricsLogger = Dependency.get(MetricsLogger.class); - private final Set<String> mNonBlockablePkgs; private final Context mContext; private final AccessibilityManager mAccessibilityManager; @@ -87,10 +86,6 @@ public class NotificationGutsManager implements Dumpable { mContext = context; Resources res = context.getResources(); - mNonBlockablePkgs = new HashSet<>(); - Collections.addAll(mNonBlockablePkgs, res.getStringArray( - com.android.internal.R.array.config_nonBlockableNotificationPackages)); - mAccessibilityManager = (AccessibilityManager) mContext.getSystemService(Context.ACCESSIBILITY_SERVICE); } @@ -279,12 +274,12 @@ public class NotificationGutsManager implements Dumpable { iNotificationManager, packageName, row.getEntry().channel, - getNumNotificationChannels(row, packageName, userHandle), + row.getNumUniqueChannels(), sbn, mCheckSaveListener, onSettingsClick, onAppSettingsClick, - mNonBlockablePkgs, + row.getIsNonblockable(), isForBlockingHelper, row.getEntry().userSentiment == USER_SENTIMENT_NEGATIVE); } catch (RemoteException e) { @@ -293,34 +288,6 @@ public class NotificationGutsManager implements Dumpable { } /** - * @return the number of channels covered by the notification row (including its children if - * it's a summary notification). - */ - private int getNumNotificationChannels( - ExpandableNotificationRow row, String packageName, UserHandle userHandle) { - ArraySet<NotificationChannel> channels = new ArraySet<>(); - - channels.add(row.getEntry().channel); - - // If this is a summary, then add in the children notification channels for the - // same user and pkg. - if (row.isSummaryWithChildren()) { - final List<ExpandableNotificationRow> childrenRows = row.getNotificationChildren(); - final int numChildren = childrenRows.size(); - for (int i = 0; i < numChildren; i++) { - final ExpandableNotificationRow childRow = childrenRows.get(i); - final NotificationChannel childChannel = childRow.getEntry().channel; - final StatusBarNotification childSbn = childRow.getStatusBarNotification(); - if (childSbn.getUser().equals(userHandle) && - childSbn.getPackageName().equals(packageName)) { - channels.add(childChannel); - } - } - } - return channels.size(); - } - - /** * Closes guts or notification menus that might be visible and saves any changes. * * @param removeLeavebehinds true if leavebehinds (e.g. snooze) should be closed. diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationInfo.java index 82ad74e26f0c..6238526a521f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationInfo.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationInfo.java @@ -31,7 +31,6 @@ import android.content.Context; import android.content.Intent; import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; -import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.graphics.drawable.Drawable; @@ -49,13 +48,11 @@ import android.widget.TextView; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; -import com.android.settingslib.Utils; import com.android.systemui.Dependency; import com.android.systemui.Interpolators; import com.android.systemui.R; import java.util.List; -import java.util.Set; /** * The guts of a notification revealed when performing a long press. @@ -74,7 +71,7 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G private int mStartingUserImportance; private int mChosenImportance; private boolean mIsSingleDefaultChannel; - private boolean mNonblockable; + private boolean mIsNonblockable; private StatusBarNotification mSbn; private AnimatorSet mExpandAnimation; private boolean mIsForeground; @@ -128,10 +125,10 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G final CheckSaveListener checkSaveListener, final OnSettingsClickListener onSettingsClick, final OnAppSettingsClickListener onAppSettingsClick, - final Set<String> nonBlockablePkgs) + boolean isNonblockable) throws RemoteException { bindNotification(pm, iNotificationManager, pkg, notificationChannel, numChannels, sbn, - checkSaveListener, onSettingsClick, onAppSettingsClick, nonBlockablePkgs, + checkSaveListener, onSettingsClick, onAppSettingsClick, isNonblockable, false /* isBlockingHelper */, false /* isUserSentimentNegative */); } @@ -146,7 +143,7 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G CheckSaveListener checkSaveListener, OnSettingsClickListener onSettingsClick, OnAppSettingsClickListener onAppSettingsClick, - Set<String> nonBlockablePkgs, + boolean isNonblockable, boolean isForBlockingHelper, boolean isUserSentimentNegative) throws RemoteException { @@ -162,6 +159,7 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G mSingleNotificationChannel = notificationChannel; mStartingUserImportance = mChosenImportance = mSingleNotificationChannel.getImportance(); mNegativeUserSentiment = isUserSentimentNegative; + mIsNonblockable = isNonblockable; mIsForeground = (mSbn.getNotification().flags & Notification.FLAG_FOREGROUND_SERVICE) != 0; mIsForBlockingHelper = isForBlockingHelper; @@ -179,22 +177,6 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G && numTotalChannels == 1; } - try { - final PackageInfo pkgInfo = pm.getPackageInfo(pkg, PackageManager.GET_SIGNATURES); - if (Utils.isSystemPackage(getResources(), pm, pkgInfo)) { - if (mSingleNotificationChannel != null - && !mSingleNotificationChannel.isBlockableSystem()) { - mNonblockable = true; - } - } - } catch (PackageManager.NameNotFoundException e) { - // unlikely. - } - if (nonBlockablePkgs != null) { - mNonblockable |= nonBlockablePkgs.contains(pkg); - } - mNonblockable |= (mNumNotificationChannels > 1); - bindHeader(); bindPrompt(); bindButtons(); @@ -261,7 +243,7 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G private void bindPrompt() { final TextView blockPrompt = findViewById(R.id.block_prompt); bindName(); - if (mNonblockable) { + if (mIsNonblockable) { blockPrompt.setText(R.string.notification_unblockable_desc); } else { if (mNegativeUserSentiment) { @@ -288,7 +270,7 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G } private void saveImportance() { - if (mNonblockable) { + if (mIsNonblockable) { return; } MetricsLogger.action(mContext, MetricsEvent.ACTION_SAVE_IMPORTANCE, @@ -314,7 +296,7 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G keep.setOnClickListener(mOnKeepShowing); minimize.setOnClickListener(mOnStopMinNotifications); - if (mNonblockable) { + if (mIsNonblockable) { keep.setText(R.string.notification_done); block.setVisibility(GONE); minimize.setVisibility(GONE); @@ -326,15 +308,15 @@ public class NotificationInfo extends LinearLayout implements NotificationGuts.G minimize.setVisibility(GONE); } - // Set up app settings link + // Set up app settings link (i.e. Customize) TextView settingsLinkView = findViewById(R.id.app_settings); Intent settingsIntent = getAppSettingsIntent(mPm, mPkg, mSingleNotificationChannel, mSbn.getId(), mSbn.getTag()); - if (settingsIntent != null + if (!mIsForBlockingHelper + && settingsIntent != null && !TextUtils.isEmpty(mSbn.getNotification().getSettingsText())) { settingsLinkView.setVisibility(VISIBLE); - settingsLinkView.setText(mContext.getString(R.string.notification_app_settings, - mSbn.getNotification().getSettingsText())); + settingsLinkView.setText(mContext.getString(R.string.notification_app_settings)); settingsLinkView.setOnClickListener((View view) -> { mAppSettingsClickListener.onClick(view, settingsIntent); }); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconContainer.java new file mode 100644 index 000000000000..56f78f419275 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconContainer.java @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar; + +import com.android.internal.statusbar.StatusBarIcon; +import java.util.ArrayList; +import java.util.List; + +/** + * Holds an array of {@link com.android.internal.statusbar.StatusBarIcon}s and draws them + * in a linear layout + */ +public class StatusBarIconContainer { + private final List<StatusBarIcon> mIcons = new ArrayList<>(); + + public StatusBarIconContainer(List<StatusBarIcon> icons) { + mIcons.addAll(icons); + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java index 603902a4507d..bd6bd12a5e9b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java @@ -16,6 +16,8 @@ package com.android.systemui.statusbar; +import static com.android.systemui.statusbar.policy.DarkIconDispatcher.getTint; + import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.ObjectAnimator; @@ -23,6 +25,7 @@ import android.animation.ValueAnimator; import android.app.Notification; import android.content.Context; import android.content.pm.ApplicationInfo; +import android.content.res.ColorStateList; import android.content.res.Configuration; import android.content.res.Resources; import android.graphics.Canvas; @@ -56,7 +59,7 @@ import com.android.systemui.statusbar.notification.NotificationUtils; import java.text.NumberFormat; import java.util.Arrays; -public class StatusBarIconView extends AnimatedImageView { +public class StatusBarIconView extends AnimatedImageView implements StatusIconDisplayable { public static final int NO_COLOR = 0; /** @@ -867,6 +870,21 @@ public class StatusBarIconView extends AnimatedImageView { mOnDismissListener = onDismissListener; } + @Override + public void onDarkChanged(Rect area, float darkIntensity, int tint) { + setImageTintList(ColorStateList.valueOf(getTint(area, this, tint))); + } + + @Override + public boolean isIconVisible() { + return mIcon != null && mIcon.visible; + } + + @Override + public boolean isIconBlocked() { + return mBlocked; + } + public interface OnVisibilityChangedListener { void onVisibilityChanged(int newVisibility); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarMobileView.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarMobileView.java new file mode 100644 index 000000000000..b7620f30d742 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarMobileView.java @@ -0,0 +1,207 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar; + +import static com.android.systemui.statusbar.policy.DarkIconDispatcher.getTint; + +import android.content.Context; +import android.content.res.ColorStateList; +import android.graphics.Color; +import android.graphics.Rect; +import android.util.AttributeSet; +import android.view.LayoutInflater; +import android.view.View; +import android.widget.ImageView; + +import com.android.internal.annotations.VisibleForTesting; +import com.android.keyguard.AlphaOptimizedLinearLayout; +import com.android.settingslib.graph.SignalDrawable; +import com.android.systemui.R; +import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.MobileIconState; +import com.android.systemui.statusbar.policy.DarkIconDispatcher.DarkReceiver; + +public class StatusBarMobileView extends AlphaOptimizedLinearLayout implements DarkReceiver, + StatusIconDisplayable { + private static final String TAG = "StatusBarMobileView"; + + private String mSlot; + private MobileIconState mState; + private SignalDrawable mMobileDrawable; + private View mInoutContainer; + private ImageView mIn; + private ImageView mOut; + private ImageView mMobile, mMobileType, mMobileRoaming; + private View mMobileRoamingSpace; + + public static StatusBarMobileView fromContext(Context context) { + LayoutInflater inflater = LayoutInflater.from(context); + + return (StatusBarMobileView) + inflater.inflate(R.layout.status_bar_mobile_signal_group, null); + } + + public StatusBarMobileView(Context context) { + super(context); + } + + public StatusBarMobileView(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public StatusBarMobileView(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + } + + public StatusBarMobileView(Context context, AttributeSet attrs, int defStyleAttr, + int defStyleRes) { + super(context, attrs, defStyleAttr, defStyleRes); + } + + @Override + protected void onFinishInflate() { + super.onFinishInflate(); + + init(); + } + + private void init() { + mMobile = findViewById(R.id.mobile_signal); + mMobileType = findViewById(R.id.mobile_type); + mMobileRoaming = findViewById(R.id.mobile_roaming); + mMobileRoamingSpace = findViewById(R.id.mobile_roaming_space); + mIn = findViewById(R.id.mobile_in); + mOut = findViewById(R.id.mobile_out); + mInoutContainer = findViewById(R.id.inout_container); + + mMobileDrawable = new SignalDrawable(getContext()); + mMobile.setImageDrawable(mMobileDrawable); + } + + public void applyMobileState(MobileIconState state) { + if (state == null) { + setVisibility(View.GONE); + mState = null; + return; + } + + if (mState == null) { + mState = state; + initViewState(); + return; + } + + if (!mState.equals(state)) { + updateState(state); + } + } + + private void initViewState() { + setContentDescription(mState.contentDescription); + if (!mState.visible) { + setVisibility(View.GONE); + } else { + setVisibility(View.VISIBLE); + } + mMobileDrawable.setLevel(mState.strengthId); + if (mState.typeId > 0) { + mMobileType.setContentDescription(mState.typeContentDescription); + mMobileType.setImageResource(mState.typeId); + mMobileType.setVisibility(View.VISIBLE); + } else { + mMobileType.setVisibility(View.GONE); + } + + mMobileRoaming.setVisibility(mState.roaming ? View.VISIBLE : View.GONE); + mMobileRoamingSpace.setVisibility(mState.roaming ? View.VISIBLE : View.GONE); + mIn.setVisibility(mState.activityIn ? View.VISIBLE : View.GONE); + mOut.setVisibility(mState.activityIn ? View.VISIBLE : View.GONE); + mInoutContainer.setVisibility((mState.activityIn || mState.activityOut) + ? View.VISIBLE : View.GONE); + } + + private void updateState(MobileIconState state) { + setContentDescription(state.contentDescription); + if (mState.visible != state.visible) { + setVisibility(state.visible ? View.VISIBLE : View.GONE); + } + if (mState.strengthId != state.strengthId) { + mMobileDrawable.setLevel(state.strengthId); + } + if (mState.typeId != state.typeId && state.typeId != 0) { + mMobileType.setContentDescription(state.typeContentDescription); + mMobileType.setImageResource(state.typeId); + mMobileType.setVisibility(View.VISIBLE); + } else { + mMobileType.setVisibility(View.GONE); + } + + mMobileRoaming.setVisibility(state.roaming ? View.VISIBLE : View.GONE); + mMobileRoamingSpace.setVisibility(state.roaming ? View.VISIBLE : View.GONE); + mIn.setVisibility(state.activityIn ? View.VISIBLE : View.GONE); + mOut.setVisibility(state.activityIn ? View.VISIBLE : View.GONE); + mInoutContainer.setVisibility((state.activityIn || state.activityOut) + ? View.VISIBLE : View.GONE); + + mState = state; + } + + @Override + public void onDarkChanged(Rect area, float darkIntensity, int tint) { + mMobileDrawable.setDarkIntensity(darkIntensity); + ColorStateList color = ColorStateList.valueOf(getTint(area, this, tint)); + mIn.setImageTintList(color); + mOut.setImageTintList(color); + mMobileType.setImageTintList(color); + mMobileRoaming.setImageTintList(color); + } + + @Override + public String getSlot() { + return mSlot; + } + + public void setSlot(String slot) { + mSlot = slot; + } + + @Override + public void setStaticDrawableColor(int color) { + ColorStateList list = ColorStateList.valueOf(color); + float intensity = color == Color.WHITE ? 0 : 1; + mMobileDrawable.setDarkIntensity(intensity); + + mIn.setImageTintList(list); + mOut.setImageTintList(list); + mMobileType.setImageTintList(list); + mMobileRoaming.setImageTintList(list); + } + + @Override + public boolean isIconVisible() { + return mState.visible; + } + + @VisibleForTesting + public MobileIconState getState() { + return mState; + } + + @Override + public String toString() { + return "StatusBarMobileView(slot=" + mSlot + " state=" + mState + ")"; + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarWifiView.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarWifiView.java new file mode 100644 index 000000000000..afd373ed6321 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarWifiView.java @@ -0,0 +1,192 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar; + +import static com.android.systemui.statusbar.policy.DarkIconDispatcher.getTint; + +import android.content.Context; +import android.content.res.ColorStateList; +import android.graphics.Rect; +import android.graphics.drawable.Drawable; +import android.util.AttributeSet; +import android.view.ContextThemeWrapper; +import android.view.LayoutInflater; +import android.view.View; +import android.widget.ImageView; + +import com.android.keyguard.AlphaOptimizedLinearLayout; +import com.android.settingslib.Utils; +import com.android.systemui.R; +import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.WifiIconState; +import com.android.systemui.statusbar.policy.DarkIconDispatcher.DarkReceiver; + +/** + * Start small: StatusBarWifiView will be able to layout from a WifiIconState + */ +public class StatusBarWifiView extends AlphaOptimizedLinearLayout implements DarkReceiver, + StatusIconDisplayable { + private static final String TAG = "StatusBarWifiView"; + + private ImageView mWifiIcon; + private ImageView mIn; + private ImageView mOut; + private View mInoutContainer; + private View mSignalSpacer; + private View mAirplaneSpacer; + private WifiIconState mState; + private String mSlot; + private float mDarkIntensity = 0; + + private ContextThemeWrapper mDarkContext; + private ContextThemeWrapper mLightContext; + + public static StatusBarWifiView fromContext(Context context) { + LayoutInflater inflater = LayoutInflater.from(context); + return (StatusBarWifiView) inflater.inflate(R.layout.status_bar_wifi_group, null); + } + + public StatusBarWifiView(Context context) { + super(context); + } + + public StatusBarWifiView(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public StatusBarWifiView(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + } + + public StatusBarWifiView(Context context, AttributeSet attrs, int defStyleAttr, + int defStyleRes) { + super(context, attrs, defStyleAttr, defStyleRes); + } + + @Override + protected void onFinishInflate() { + super.onFinishInflate(); + init(); + } + + public void setSlot(String slot) { + mSlot = slot; + } + + @Override + public void setStaticDrawableColor(int color) { + ColorStateList list = ColorStateList.valueOf(color); + mWifiIcon.setImageTintList(list); + mIn.setImageTintList(list); + mOut.setImageTintList(list); + } + + @Override + public String getSlot() { + return mSlot; + } + + @Override + public boolean isIconVisible() { + return mState != null && mState.visible; + } + + private void init() { + int dualToneLightTheme = Utils.getThemeAttr(mContext, R.attr.lightIconTheme); + int dualToneDarkTheme = Utils.getThemeAttr(mContext, R.attr.darkIconTheme); + mLightContext = new ContextThemeWrapper(mContext, dualToneLightTheme); + mDarkContext = new ContextThemeWrapper(mContext, dualToneDarkTheme); + + mWifiIcon = findViewById(R.id.wifi_signal); + mIn = findViewById(R.id.wifi_in); + mOut = findViewById(R.id.wifi_out); + mSignalSpacer = findViewById(R.id.wifi_signal_spacer); + mAirplaneSpacer = findViewById(R.id.wifi_airplane_spacer); + mInoutContainer = findViewById(R.id.inout_container); + } + + public void applyWifiState(WifiIconState state) { + if (state == null) { + setVisibility(View.GONE); + mState = null; + return; + } + + if (mState == null) { + mState = state; + initViewState(); + } + + if (!mState.equals(state)) { + updateState(state); + } + } + + private void updateState(WifiIconState state) { + if (mState.resId != state.resId && state.resId >= 0) { + NeutralGoodDrawable drawable = NeutralGoodDrawable + .create(mLightContext, mDarkContext, state.resId); + drawable.setDarkIntensity(mDarkIntensity); + mWifiIcon.setImageDrawable(drawable); + } + + mIn.setVisibility(state.activityIn ? View.VISIBLE : View.GONE); + mOut.setVisibility(state.activityOut ? View.VISIBLE : View.GONE); + mInoutContainer.setVisibility( + (state.activityIn || state.activityOut) ? View.VISIBLE : View.GONE); + mAirplaneSpacer.setVisibility(state.airplaneSpacerVisible ? View.VISIBLE : View.GONE); + mSignalSpacer.setVisibility(state.signalSpacerVisible ? View.VISIBLE : View.GONE); + if (mState.visible != state.visible) { + setVisibility(state.visible ? View.VISIBLE : View.GONE); + } + + mState = state; + } + + private void initViewState() { + if (mState.resId >= 0) { + NeutralGoodDrawable drawable = NeutralGoodDrawable.create( + mLightContext, mDarkContext, mState.resId); + drawable.setDarkIntensity(mDarkIntensity); + mWifiIcon.setImageDrawable(drawable); + } + + mIn.setVisibility(mState.activityIn ? View.VISIBLE : View.GONE); + mOut.setVisibility(mState.activityOut ? View.VISIBLE : View.GONE); + mInoutContainer.setVisibility( + (mState.activityIn || mState.activityOut) ? View.VISIBLE : View.GONE); + mAirplaneSpacer.setVisibility(mState.airplaneSpacerVisible ? View.VISIBLE : View.GONE); + mSignalSpacer.setVisibility(mState.signalSpacerVisible ? View.VISIBLE : View.GONE); + setVisibility(mState.visible ? View.VISIBLE : View.GONE); + } + + @Override + public void onDarkChanged(Rect area, float darkIntensity, int tint) { + mDarkIntensity = darkIntensity; + Drawable d = mWifiIcon.getDrawable(); + if (d instanceof NeutralGoodDrawable) { + ((NeutralGoodDrawable)d).setDarkIntensity(darkIntensity); + } + mIn.setImageTintList(ColorStateList.valueOf(getTint(area, this, tint))); + mOut.setImageTintList(ColorStateList.valueOf(getTint(area, this, tint))); + } + + + @Override + public String toString() { + return "StatusBarWifiView(slot=" + mSlot + " state=" + mState + ")"; + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusIconDisplayable.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusIconDisplayable.java new file mode 100644 index 000000000000..ccab0d6fa44b --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusIconDisplayable.java @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar; + +import com.android.systemui.statusbar.policy.DarkIconDispatcher.DarkReceiver; + +public interface StatusIconDisplayable extends DarkReceiver { + String getSlot(); + void setStaticDrawableColor(int color); + boolean isIconVisible(); + default boolean isIconBlocked() { + return false; + } +} + diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarFacetButtonController.java b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarFacetButtonController.java index 284113624cc1..b7d501e7f745 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarFacetButtonController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarFacetButtonController.java @@ -46,6 +46,12 @@ public class CarFacetButtonController { } } + public void removeAll() { + mButtonsByCategory.clear(); + mButtonsByPackage.clear(); + mSelectedFacetButton = null; + } + /** * This will unselect the currently selected CarFacetButton and determine which one should be * selected next. It does this by reading the properties on the CarFacetButton and seeing if diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java index a95d0a4dc7b8..3530e0b84664 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java @@ -45,6 +45,7 @@ import com.android.systemui.statusbar.car.hvac.HvacController; import com.android.systemui.statusbar.phone.CollapsedStatusBarFragment; import com.android.systemui.statusbar.phone.StatusBar; import com.android.systemui.statusbar.policy.BatteryController; +import com.android.systemui.statusbar.policy.DeviceProvisionedController; import com.android.systemui.statusbar.policy.UserSwitcherController; import java.io.FileDescriptor; @@ -80,12 +81,16 @@ public class CarStatusBar extends StatusBar implements private boolean mShowRight; private boolean mShowBottom; private CarFacetButtonController mCarFacetButtonController; + private ActivityManagerWrapper mActivityManagerWrapper; + private DeviceProvisionedController mDeviceProvisionedController; + private boolean mDeviceIsProvisioned = true; @Override public void start() { super.start(); mTaskStackListener = new TaskStackListenerImpl(); - ActivityManagerWrapper.getInstance().registerTaskStackListener(mTaskStackListener); + mActivityManagerWrapper = ActivityManagerWrapper.getInstance(); + mActivityManagerWrapper.registerTaskStackListener(mTaskStackListener); mStackScroller.setScrollingEnabled(true); @@ -96,12 +101,54 @@ public class CarStatusBar extends StatusBar implements Log.d(TAG, "Connecting to HVAC service"); Dependency.get(HvacController.class).connectToCarService(); } + mCarFacetButtonController = Dependency.get(CarFacetButtonController.class); + mDeviceProvisionedController = Dependency.get(DeviceProvisionedController.class); + mDeviceIsProvisioned = mDeviceProvisionedController.isDeviceProvisioned(); + if (!mDeviceIsProvisioned) { + mDeviceProvisionedController.addCallback( + new DeviceProvisionedController.DeviceProvisionedListener() { + @Override + public void onDeviceProvisionedChanged() { + mDeviceIsProvisioned = + mDeviceProvisionedController.isDeviceProvisioned(); + restartNavBars(); + } + }); + } + } + + /** + * Remove all content from navbars and rebuild them. Used to allow for different nav bars + * before and after the device is provisioned + */ + private void restartNavBars() { + mCarFacetButtonController.removeAll(); + if (ENABLE_HVAC_CONNECTION) { + Dependency.get(HvacController.class).removeAllComponents(); + } + if (mNavigationBarWindow != null) { + mNavigationBarWindow.removeAllViews(); + mNavigationBarView = null; + } + + if (mLeftNavigationBarWindow != null) { + mLeftNavigationBarWindow.removeAllViews(); + mLeftNavigationBarView = null; + } + + if (mRightNavigationBarWindow != null) { + mRightNavigationBarWindow.removeAllViews(); + mRightNavigationBarView = null; + } + buildNavBarContent(); } + @Override public void destroy() { mCarBatteryController.stopListening(); mConnectedDeviceSignalController.stopListening(); + mActivityManagerWrapper.unregisterTaskStackListener(mTaskStackListener); if (mNavigationBarWindow != null) { mWindowManager.removeViewImmediate(mNavigationBarWindow); @@ -117,10 +164,10 @@ public class CarStatusBar extends StatusBar implements mWindowManager.removeViewImmediate(mRightNavigationBarWindow); mRightNavigationBarView = null; } - super.destroy(); } + @Override protected void makeStatusBarView() { super.makeStatusBarView(); @@ -167,129 +214,132 @@ public class CarStatusBar extends StatusBar implements @Override protected void createNavigationBar() { - mCarFacetButtonController = Dependency.get(CarFacetButtonController.class); - if (mNavigationBarView != null) { - return; - } - mShowBottom = mContext.getResources().getBoolean(R.bool.config_enableBottomNavigationBar); + mShowLeft = mContext.getResources().getBoolean(R.bool.config_enableLeftNavigationBar); + mShowRight = mContext.getResources().getBoolean(R.bool.config_enableRightNavigationBar); + + buildNavBarWindows(); + buildNavBarContent(); + attachNavBarWindows(); + } + + private void buildNavBarContent() { if (mShowBottom) { - buildBottomBar(); + buildBottomBar((mDeviceIsProvisioned) ? R.layout.car_navigation_bar : + R.layout.car_navigation_bar_unprovisioned); } - int widthForSides = mContext.getResources().getDimensionPixelSize( - R.dimen.navigation_bar_height_car_mode); + if (mShowLeft) { + buildLeft((mDeviceIsProvisioned) ? R.layout.car_left_navigation_bar : + R.layout.car_left_navigation_bar_unprovisioned); + } + if (mShowRight) { + buildRight((mDeviceIsProvisioned) ? R.layout.car_right_navigation_bar : + R.layout.car_right_navigation_bar_unprovisioned); + } + } - mShowLeft = mContext.getResources().getBoolean(R.bool.config_enableLeftNavigationBar); + private void buildNavBarWindows() { + if (mShowBottom) { + mNavigationBarWindow = (ViewGroup) View.inflate(mContext, + R.layout.navigation_bar_window, null); + } if (mShowLeft) { - buildLeft(widthForSides); + mLeftNavigationBarWindow = (ViewGroup) View.inflate(mContext, + R.layout.navigation_bar_window, null); + } + if (mShowRight) { + mRightNavigationBarWindow = (ViewGroup) View.inflate(mContext, + R.layout.navigation_bar_window, null); } - mShowRight = mContext.getResources().getBoolean(R.bool.config_enableRightNavigationBar); + } + + private void attachNavBarWindows() { + if (mShowBottom) { + WindowManager.LayoutParams lp = new WindowManager.LayoutParams( + LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT, + WindowManager.LayoutParams.TYPE_NAVIGATION_BAR, + WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE + | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL + | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH + | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH, + PixelFormat.TRANSLUCENT); + lp.setTitle("CarNavigationBar"); + lp.windowAnimations = 0; + mWindowManager.addView(mNavigationBarWindow, lp); + } + if (mShowLeft) { + int width = mContext.getResources().getDimensionPixelSize( + R.dimen.car_left_navigation_bar_width); + WindowManager.LayoutParams leftlp = new WindowManager.LayoutParams( + width, LayoutParams.MATCH_PARENT, + WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL, + WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE + | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL + | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH + | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH, + PixelFormat.TRANSLUCENT); + leftlp.setTitle("LeftCarNavigationBar"); + leftlp.windowAnimations = 0; + leftlp.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_IS_SCREEN_DECOR; + leftlp.gravity = Gravity.LEFT; + mWindowManager.addView(mLeftNavigationBarWindow, leftlp); + } if (mShowRight) { - buildRight(widthForSides); + int width = mContext.getResources().getDimensionPixelSize( + R.dimen.car_right_navigation_bar_width); + WindowManager.LayoutParams rightlp = new WindowManager.LayoutParams( + width, LayoutParams.MATCH_PARENT, + WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL, + WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE + | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL + | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH + | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH, + PixelFormat.TRANSLUCENT); + rightlp.setTitle("RightCarNavigationBar"); + rightlp.windowAnimations = 0; + rightlp.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_IS_SCREEN_DECOR; + rightlp.gravity = Gravity.RIGHT; + mWindowManager.addView(mRightNavigationBarWindow, rightlp); } } - - private void buildBottomBar() { + private void buildBottomBar(int layout) { // SystemUI requires that the navigation bar view have a parent. Since the regular // StatusBar inflates navigation_bar_window as this parent view, use the same view for the // CarNavigationBarView. - mNavigationBarWindow = (ViewGroup) View.inflate(mContext, - R.layout.navigation_bar_window, null); - if (mNavigationBarWindow == null) { - Log.e(TAG, "CarStatusBar failed inflate for R.layout.navigation_bar_window"); - } - - - View.inflate(mContext, R.layout.car_navigation_bar, mNavigationBarWindow); + View.inflate(mContext, layout, mNavigationBarWindow); mNavigationBarView = (CarNavigationBarView) mNavigationBarWindow.getChildAt(0); if (mNavigationBarView == null) { Log.e(TAG, "CarStatusBar failed inflate for R.layout.car_navigation_bar"); throw new RuntimeException("Unable to build botom nav bar due to missing layout"); } mNavigationBarView.setStatusBar(this); - - - WindowManager.LayoutParams lp = new WindowManager.LayoutParams( - LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT, - WindowManager.LayoutParams.TYPE_NAVIGATION_BAR, - WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE - | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL - | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH - | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH, - PixelFormat.TRANSLUCENT); - lp.setTitle("CarNavigationBar"); - lp.windowAnimations = 0; - - - mWindowManager.addView(mNavigationBarWindow, lp); } - private void buildLeft(int widthForSides) { - mLeftNavigationBarWindow = (ViewGroup) View.inflate(mContext, - R.layout.navigation_bar_window, null); - if (mLeftNavigationBarWindow == null) { - Log.e(TAG, "CarStatusBar failed inflate for R.layout.navigation_bar_window"); - } - - View.inflate(mContext, R.layout.car_left_navigation_bar, mLeftNavigationBarWindow); + private void buildLeft(int layout) { + View.inflate(mContext, layout, mLeftNavigationBarWindow); mLeftNavigationBarView = (CarNavigationBarView) mLeftNavigationBarWindow.getChildAt(0); if (mLeftNavigationBarView == null) { Log.e(TAG, "CarStatusBar failed inflate for R.layout.car_navigation_bar"); throw new RuntimeException("Unable to build left nav bar due to missing layout"); } mLeftNavigationBarView.setStatusBar(this); - - WindowManager.LayoutParams leftlp = new WindowManager.LayoutParams( - widthForSides, LayoutParams.MATCH_PARENT, - WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL, - WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE - | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL - | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH - | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH, - PixelFormat.TRANSLUCENT); - leftlp.setTitle("LeftCarNavigationBar"); - leftlp.windowAnimations = 0; - leftlp.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_IS_SCREEN_DECOR; - leftlp.gravity = Gravity.LEFT; - mWindowManager.addView(mLeftNavigationBarWindow, leftlp); } - private void buildRight(int widthForSides) { - mRightNavigationBarWindow = (ViewGroup) View.inflate(mContext, - R.layout.navigation_bar_window, null); - if (mRightNavigationBarWindow == null) { - Log.e(TAG, "CarStatusBar failed inflate for R.layout.navigation_bar_window"); - } - - View.inflate(mContext, R.layout.car_right_navigation_bar, mRightNavigationBarWindow); + private void buildRight(int layout) { + View.inflate(mContext, layout, mRightNavigationBarWindow); mRightNavigationBarView = (CarNavigationBarView) mRightNavigationBarWindow.getChildAt(0); if (mRightNavigationBarView == null) { Log.e(TAG, "CarStatusBar failed inflate for R.layout.car_navigation_bar"); throw new RuntimeException("Unable to build right nav bar due to missing layout"); } - mRightNavigationBarView.setStatusBar(this); - - WindowManager.LayoutParams rightlp = new WindowManager.LayoutParams( - widthForSides, LayoutParams.MATCH_PARENT, - WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL, - WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE - | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL - | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH - | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH, - PixelFormat.TRANSLUCENT); - rightlp.setTitle("RightCarNavigationBar"); - rightlp.windowAnimations = 0; - rightlp.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_IS_SCREEN_DECOR; - rightlp.gravity = Gravity.RIGHT; - mWindowManager.addView(mRightNavigationBarWindow, rightlp); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/hvac/HvacController.java b/packages/SystemUI/src/com/android/systemui/statusbar/car/hvac/HvacController.java index 23bf88796da3..7d283d9fde9d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/car/hvac/HvacController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/car/hvac/HvacController.java @@ -176,6 +176,14 @@ public class HvacController { }; /** + * Removes all registered components. This is useful if you need to rebuild the UI since + * components self register. + */ + public void removeAllComponents() { + mTempComponents.clear(); + } + + /** * Key for storing {@link TemperatureView}s in a hash map */ private static class HvacKey { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java index f42473d4cddf..75b31c5a1edd 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CollapsedStatusBarFragment.java @@ -17,8 +17,6 @@ package com.android.systemui.statusbar.phone; import static android.app.StatusBarManager.DISABLE_NOTIFICATION_ICONS; import static android.app.StatusBarManager.DISABLE_SYSTEM_INFO; -import static com.android.systemui.statusbar.phone.StatusBar.reinflateSignalCluster; - import android.annotation.Nullable; import android.app.Fragment; import android.app.StatusBarManager; @@ -34,7 +32,6 @@ import com.android.systemui.Interpolators; import com.android.systemui.R; import com.android.systemui.SysUiServiceProvider; import com.android.systemui.statusbar.CommandQueue; -import com.android.systemui.statusbar.SignalClusterView; import com.android.systemui.statusbar.phone.StatusBarIconController.DarkIconManager; import com.android.systemui.statusbar.policy.DarkIconDispatcher; import com.android.systemui.statusbar.policy.EncryptionHelper; @@ -63,7 +60,6 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue private int mDisabled1; private StatusBar mStatusBarComponent; private DarkIconManager mDarkIconManager; - private SignalClusterView mSignalClusterView; private View mOperatorNameFrame; private SignalCallback mSignalCallback = new SignalCallback() { @@ -99,9 +95,6 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue Dependency.get(StatusBarIconController.class).addIconGroup(mDarkIconManager); mSystemIconArea = mStatusBar.findViewById(R.id.system_icon_area); mClockView = mStatusBar.findViewById(R.id.clock); - mSignalClusterView = mStatusBar.findViewById(R.id.signal_cluster); - Dependency.get(DarkIconDispatcher.class).addDarkReceiver(mSignalClusterView); - // Default to showing until we know otherwise. showSystemIconArea(false); initEmergencyCryptkeeperText(); initOperatorName(); @@ -128,7 +121,6 @@ public class CollapsedStatusBarFragment extends Fragment implements CommandQueue @Override public void onDestroyView() { super.onDestroyView(); - Dependency.get(DarkIconDispatcher.class).removeDarkReceiver(mSignalClusterView); Dependency.get(StatusBarIconController.class).removeIconGroup(mDarkIconManager); if (mNetworkController.hasEmergencyCryptKeeperText()) { mNetworkController.removeCallback(mSignalCallback); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DarkIconDispatcherImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DarkIconDispatcherImpl.java index 3f9ae8023149..80c4eb043d24 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DarkIconDispatcherImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DarkIconDispatcherImpl.java @@ -69,7 +69,7 @@ public class DarkIconDispatcherImpl implements DarkIconDispatcher { mReceivers.remove(object); } - public void applyDark(ImageView object) { + public void applyDark(DarkReceiver object) { mReceivers.get(object).onDarkChanged(mTintArea, mDarkIntensity, mIconTint); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DemoStatusIcons.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DemoStatusIcons.java index edfd02bdfb26..48540b111609 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DemoStatusIcons.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DemoStatusIcons.java @@ -20,25 +20,33 @@ import android.graphics.Rect; import android.graphics.drawable.Icon; import android.os.Bundle; import android.os.UserHandle; +import android.util.Log; import android.view.Gravity; import android.view.View; import android.view.ViewGroup; import android.widget.LinearLayout; import com.android.internal.statusbar.StatusBarIcon; -import com.android.settingslib.Utils; import com.android.systemui.DemoMode; import com.android.systemui.R; +import com.android.systemui.statusbar.StatusIconDisplayable; import com.android.systemui.statusbar.StatusBarIconView; +import com.android.systemui.statusbar.StatusBarMobileView; +import com.android.systemui.statusbar.StatusBarWifiView; +import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.MobileIconState; +import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.WifiIconState; import com.android.systemui.statusbar.policy.DarkIconDispatcher; import com.android.systemui.statusbar.policy.DarkIconDispatcher.DarkReceiver; -import com.android.systemui.statusbar.policy.LocationControllerImpl; -import com.android.systemui.util.leak.LeakDetector; +import java.util.ArrayList; public class DemoStatusIcons extends StatusIconContainer implements DemoMode, DarkReceiver { + private static final String TAG = "DemoStatusIcons"; + private final LinearLayout mStatusIcons; + private final ArrayList<StatusBarMobileView> mMobileViews = new ArrayList<>(); private final int mIconSize; + private StatusBarWifiView mWifiView; private boolean mDemoMode; private int mColor; @@ -56,6 +64,7 @@ public class DemoStatusIcons extends StatusIconContainer implements DemoMode, Da } public void remove() { + mMobileViews.clear(); ((ViewGroup) getParent()).removeView(this); } @@ -66,7 +75,7 @@ public class DemoStatusIcons extends StatusIconContainer implements DemoMode, Da private void updateColors() { for (int i = 0; i < getChildCount(); i++) { - StatusBarIconView child = (StatusBarIconView) getChildAt(i); + StatusIconDisplayable child = (StatusIconDisplayable) getChildAt(i); child.setStaticDrawableColor(mColor); } } @@ -145,6 +154,7 @@ public class DemoStatusIcons extends StatusIconContainer implements DemoMode, Da } } + /// Can only be used to update non-signal related slots private void updateSlot(String slot, String iconPkg, int iconId) { if (!mDemoMode) return; if (iconPkg == null) { @@ -152,7 +162,11 @@ public class DemoStatusIcons extends StatusIconContainer implements DemoMode, Da } int removeIndex = -1; for (int i = 0; i < getChildCount(); i++) { - StatusBarIconView v = (StatusBarIconView) getChildAt(i); + View child = getChildAt(i); + if (!(child instanceof StatusBarIconView)) { + continue; + } + StatusBarIconView v = (StatusBarIconView) child; if (slot.equals(v.getTag())) { if (iconId == 0) { removeIndex = i; @@ -182,8 +196,101 @@ public class DemoStatusIcons extends StatusIconContainer implements DemoMode, Da addView(v, 0, new LinearLayout.LayoutParams(mIconSize, mIconSize)); } + public void addDemoWifiView(WifiIconState state) { + Log.d(TAG, "addDemoWifiView: "); + StatusBarWifiView view = StatusBarWifiView.fromContext(mContext); + view.setSlot(state.slot); + + int viewIndex = getChildCount(); + // If we have mobile views, put wifi before them + for (int i = 0; i < getChildCount(); i++) { + View child = getChildAt(i); + if (child instanceof StatusBarMobileView) { + viewIndex = i; + break; + } + } + + mWifiView = view; + mWifiView.applyWifiState(state); + mWifiView.setStaticDrawableColor(mColor); + addView(view, viewIndex); + } + + public void updateWifiState(WifiIconState state) { + Log.d(TAG, "updateWifiState: "); + if (mWifiView == null) { + addDemoWifiView(state); + } else { + mWifiView.applyWifiState(state); + } + } + + public void addMobileView(MobileIconState state) { + Log.d(TAG, "addMobileView: "); + StatusBarMobileView view = StatusBarMobileView.fromContext(mContext); + + view.setSlot(state.slot); + view.applyMobileState(state); + view.setStaticDrawableColor(mColor); + + // mobile always goes at the end + mMobileViews.add(view); + addView(view, getChildCount()); + } + + public void updateMobileState(MobileIconState state) { + Log.d(TAG, "updateMobileState: "); + // If the view for this subId exists already, use it + for (int i = 0; i < mMobileViews.size(); i++) { + StatusBarMobileView view = mMobileViews.get(i); + if (view.getState().subId == state.subId) { + view.applyMobileState(state); + return; + } + } + + // Else we have to add it + addMobileView(state); + } + + public void onRemoveIcon(StatusIconDisplayable view) { + if (view.getSlot().equals("wifi")) { + removeView(mWifiView); + mWifiView = null; + } else { + StatusBarMobileView mobileView = matchingMobileView(view); + if (mobileView != null) { + removeView(mobileView); + mMobileViews.remove(mobileView); + } + } + } + + private StatusBarMobileView matchingMobileView(StatusIconDisplayable otherView) { + if (!(otherView instanceof StatusBarMobileView)) { + return null; + } + + StatusBarMobileView v = (StatusBarMobileView) otherView; + for (StatusBarMobileView view : mMobileViews) { + if (view.getState().subId == v.getState().subId) { + return view; + } + } + + return null; + } + @Override public void onDarkChanged(Rect area, float darkIntensity, int tint) { setColor(DarkIconDispatcher.getTint(area, mStatusIcons, tint)); + + if (mWifiView != null) { + mWifiView.onDarkChanged(area, darkIntensity, tint); + } + for (StatusBarMobileView view : mMobileViews) { + view.onDarkChanged(area, darkIntensity, tint); + } } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java index 994c0abcf70b..b81780962f08 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java @@ -207,7 +207,6 @@ public class KeyguardStatusBarView extends RelativeLayout @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { updateLayoutConsideringCutout(); - setSignalClusterLayoutWidth(); super.onMeasure(widthMeasureSpec, heightMeasureSpec); } @@ -296,17 +295,6 @@ public class KeyguardStatusBarView extends RelativeLayout return true; } - //TODO: Something is setting signal_cluster to MATCH_PARENT. Why? - private void setSignalClusterLayoutWidth() { - View signalCluster = findViewById(R.id.signal_cluster); - if (signalCluster == null) { - return; - } - - LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) signalCluster.getLayoutParams(); - lp.width = LinearLayout.LayoutParams.WRAP_CONTENT; - } - public void setListening(boolean listening) { if (listening == mBatteryListening) { return; @@ -459,7 +447,6 @@ public class KeyguardStatusBarView extends RelativeLayout mIconManager.setTint(iconColor); Rect tintArea = new Rect(0, 0, 0, 0); - applyDarkness(R.id.signal_cluster, tintArea, intensity, iconColor); applyDarkness(R.id.battery, tintArea, intensity, iconColor); applyDarkness(R.id.clock, tintArea, intensity, iconColor); // Reload user avatar diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java index 33c3ee9b7cba..3e7b0d9f62d0 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java @@ -297,7 +297,7 @@ public class PhoneStatusBarPolicy implements Callback, Callbacks, mIconController.setIcon(mSlotLocation, LOCATION_STATUS_ICON_ID, mContext.getString(R.string.accessibility_location_active)); } else { - mIconController.removeIcon(mSlotLocation); + mIconController.removeAllIconsForSlot(mSlotLocation); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarTransitions.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarTransitions.java index fb1addfeb1c3..12bdfc674f04 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarTransitions.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarTransitions.java @@ -32,7 +32,7 @@ public final class PhoneStatusBarTransitions extends BarTransitions { private final PhoneStatusBarView mView; private final float mIconAlphaWhenOpaque; - private View mLeftSide, mStatusIcons, mSignalCluster, mBattery, mClock; + private View mLeftSide, mStatusIcons, mBattery, mClock; private Animator mCurrentAnimation; public PhoneStatusBarTransitions(PhoneStatusBarView view) { @@ -45,7 +45,6 @@ public final class PhoneStatusBarTransitions extends BarTransitions { public void init() { mLeftSide = mView.findViewById(R.id.notification_icon_area); mStatusIcons = mView.findViewById(R.id.statusIcons); - mSignalCluster = mView.findViewById(R.id.signal_cluster); mBattery = mView.findViewById(R.id.battery); mClock = mView.findViewById(R.id.clock); applyModeBackground(-1, getMode(), false /*animate*/); @@ -90,7 +89,6 @@ public final class PhoneStatusBarTransitions extends BarTransitions { anims.playTogether( animateTransitionTo(mLeftSide, newAlpha), animateTransitionTo(mStatusIcons, newAlpha), - animateTransitionTo(mSignalCluster, newAlpha), animateTransitionTo(mBattery, newAlphaBC), animateTransitionTo(mClock, newAlphaBC) ); @@ -102,7 +100,6 @@ public final class PhoneStatusBarTransitions extends BarTransitions { } else { mLeftSide.setAlpha(newAlpha); mStatusIcons.setAlpha(newAlpha); - mSignalCluster.setAlpha(newAlpha); mBattery.setAlpha(newAlphaBC); mClock.setAlpha(newAlphaBC); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java index beeba83b9c25..750d2a5b3d50 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java @@ -348,6 +348,7 @@ public class StatusBar extends SystemUI implements DemoMode, protected boolean mBouncerShowing; private PhoneStatusBarPolicy mIconPolicy; + private StatusBarSignalPolicy mSignalPolicy; private VolumeComponent mVolumeComponent; private BrightnessMirrorController mBrightnessMirrorController; @@ -747,6 +748,7 @@ public class StatusBar extends SystemUI implements DemoMode, // Lastly, call to the icon policy to install/update all the icons. mIconPolicy = new PhoneStatusBarPolicy(mContext, mIconController); + mSignalPolicy = new StatusBarSignalPolicy(mContext, mIconController); mUnlockMethodCache = UnlockMethodCache.getInstance(mContext); mUnlockMethodCache.addListener(this); @@ -1131,7 +1133,6 @@ public class StatusBar extends SystemUI implements DemoMode, } protected void reevaluateStyles() { - inflateSignalClusters(); inflateFooterView(); updateFooter(); inflateEmptyShadeView(); @@ -1145,36 +1146,6 @@ public class StatusBar extends SystemUI implements DemoMode, } } - private void inflateSignalClusters() { - if (mKeyguardStatusBar != null) reinflateSignalCluster(mKeyguardStatusBar); - } - - public static SignalClusterView reinflateSignalCluster(View view) { - Context context = view.getContext(); - SignalClusterView signalCluster = view.findViewById(R.id.signal_cluster); - if (signalCluster != null) { - ViewParent parent = signalCluster.getParent(); - if (parent instanceof ViewGroup) { - ViewGroup viewParent = (ViewGroup) parent; - int index = viewParent.indexOfChild(signalCluster); - viewParent.removeView(signalCluster); - SignalClusterView newCluster = (SignalClusterView) LayoutInflater.from(context) - .inflate(R.layout.signal_cluster_view, viewParent, false); - ViewGroup.MarginLayoutParams layoutParams = - (ViewGroup.MarginLayoutParams) viewParent.getLayoutParams(); - layoutParams.setMarginsRelative( - context.getResources().getDimensionPixelSize( - R.dimen.signal_cluster_margin_start), - 0, 0, 0); - newCluster.setLayoutParams(layoutParams); - viewParent.addView(newCluster, index); - return newCluster; - } - return signalCluster; - } - return null; - } - private void inflateEmptyShadeView() { if (mStackScroller == null) { return; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java index 956bebb6ca41..94e004bd1b47 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java @@ -16,12 +16,16 @@ package com.android.systemui.statusbar.phone; import static android.app.StatusBarManager.DISABLE2_SYSTEM_ICONS; import static android.app.StatusBarManager.DISABLE_NONE; +import static com.android.systemui.statusbar.phone.StatusBarIconHolder.TYPE_ICON; +import static com.android.systemui.statusbar.phone.StatusBarIconHolder.TYPE_MOBILE; +import static com.android.systemui.statusbar.phone.StatusBarIconHolder.TYPE_WIFI; import android.content.Context; import android.os.Bundle; import android.support.annotation.VisibleForTesting; import android.text.TextUtils; import android.util.ArraySet; +import android.util.Log; import android.view.Gravity; import android.view.View; import android.view.ViewGroup; @@ -33,19 +37,39 @@ import com.android.internal.statusbar.StatusBarIcon; import com.android.systemui.DemoMode; import com.android.systemui.Dependency; import com.android.systemui.R; +import com.android.systemui.statusbar.StatusIconDisplayable; import com.android.systemui.statusbar.StatusBarIconView; +import com.android.systemui.statusbar.StatusBarMobileView; +import com.android.systemui.statusbar.StatusBarWifiView; +import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.MobileIconState; +import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.WifiIconState; import com.android.systemui.statusbar.policy.DarkIconDispatcher; +import com.android.systemui.statusbar.policy.DarkIconDispatcher.DarkReceiver; import com.android.systemui.util.Utils.DisableStateTracker; +import java.util.List; public interface StatusBarIconController { + /** + * When an icon is added with TAG_PRIMARY, it will be treated as the primary icon + * in that slot and not added as a sub slot. + */ + public static final int TAG_PRIMARY = 0; + public void addIconGroup(IconManager iconManager); public void removeIconGroup(IconManager iconManager); public void setExternalIcon(String slot); public void setIcon(String slot, int resourceId, CharSequence contentDescription); public void setIcon(String slot, StatusBarIcon icon); - public void setIconVisibility(String slotTty, boolean b); - public void removeIcon(String slot); + public void setSignalIcon(String slot, WifiIconState state); + public void setMobileIcons(String slot, List<MobileIconState> states); + public void setIconVisibility(String slot, boolean b); + /** + * If you don't know what to pass for `tag`, either remove all icons for slot, or use + * TAG_PRIMARY to refer to the first icon at a given slot. + */ + public void removeIcon(String slot, int tag); + public void removeAllIconsForSlot(String slot); public static final String ICON_BLACKLIST = "icon_blacklist"; @@ -79,9 +103,9 @@ public interface StatusBarIconController { @Override protected void onIconAdded(int index, String slot, boolean blocked, - StatusBarIcon icon) { - StatusBarIconView v = addIcon(index, slot, blocked, icon); - mDarkIconDispatcher.addDarkReceiver(v); + StatusBarIconHolder holder) { + StatusIconDisplayable view = addHolder(index, slot, blocked, holder); + mDarkIconDispatcher.addDarkReceiver((DarkReceiver) view); } @Override @@ -95,21 +119,21 @@ public interface StatusBarIconController { @Override protected void destroy() { for (int i = 0; i < mGroup.getChildCount(); i++) { - mDarkIconDispatcher.removeDarkReceiver((ImageView) mGroup.getChildAt(i)); + mDarkIconDispatcher.removeDarkReceiver((DarkReceiver) mGroup.getChildAt(i)); } mGroup.removeAllViews(); } @Override protected void onRemoveIcon(int viewIndex) { - mDarkIconDispatcher.removeDarkReceiver((ImageView) mGroup.getChildAt(viewIndex)); + mDarkIconDispatcher.removeDarkReceiver((DarkReceiver) mGroup.getChildAt(viewIndex)); super.onRemoveIcon(viewIndex); } @Override public void onSetIcon(int viewIndex, StatusBarIcon icon) { super.onSetIcon(viewIndex, icon); - mDarkIconDispatcher.applyDark((ImageView) mGroup.getChildAt(viewIndex)); + mDarkIconDispatcher.applyDark((DarkReceiver) mGroup.getChildAt(viewIndex)); } @Override @@ -135,17 +159,18 @@ public interface StatusBarIconController { } @Override - protected void onIconAdded(int index, String slot, boolean blocked, StatusBarIcon icon) { - StatusBarIconView v = addIcon(index, slot, blocked, icon); - v.setStaticDrawableColor(mColor); + protected void onIconAdded(int index, String slot, boolean blocked, + StatusBarIconHolder holder) { + StatusIconDisplayable view = addHolder(index, slot, blocked, holder); + view.setStaticDrawableColor(mColor); } public void setTint(int color) { mColor = color; for (int i = 0; i < mGroup.getChildCount(); i++) { View child = mGroup.getChildAt(i); - if (child instanceof StatusBarIconView) { - StatusBarIconView icon = (StatusBarIconView) child; + if (child instanceof StatusIconDisplayable) { + StatusIconDisplayable icon = (StatusIconDisplayable) child; icon.setStaticDrawableColor(mColor); } } @@ -171,6 +196,7 @@ public interface StatusBarIconController { // Enables SystemUI demo mode to take effect in this group protected boolean mDemoable = true; + private boolean mIsInDemoMode; protected DemoStatusIcons mDemoStatusIcons; public IconManager(ViewGroup group) { @@ -205,10 +231,27 @@ public interface StatusBarIconController { } protected void onIconAdded(int index, String slot, boolean blocked, - StatusBarIcon icon) { - addIcon(index, slot, blocked, icon); + StatusBarIconHolder holder) { + addHolder(index, slot, blocked, holder); + } + + protected StatusIconDisplayable addHolder(int index, String slot, boolean blocked, + StatusBarIconHolder holder) { + switch (holder.getType()) { + case TYPE_ICON: + return addIcon(index, slot, blocked, holder.getIcon()); + + case TYPE_WIFI: + return addSignalIcon(index, slot, holder.getWifiState()); + + case TYPE_MOBILE: + return addMobileIcon(index, slot, holder.getMobileState()); + } + + return null; } + @VisibleForTesting protected StatusBarIconView addIcon(int index, String slot, boolean blocked, StatusBarIcon icon) { StatusBarIconView view = onCreateStatusBarIconView(slot, blocked); @@ -218,10 +261,45 @@ public interface StatusBarIconController { } @VisibleForTesting - protected StatusBarIconView onCreateStatusBarIconView(String slot, boolean blocked) { + protected StatusBarWifiView addSignalIcon(int index, String slot, WifiIconState state) { + StatusBarWifiView view = onCreateStatusBarWifiView(slot); + view.applyWifiState(state); + mGroup.addView(view, index, onCreateLayoutParams()); + + if (mIsInDemoMode) { + mDemoStatusIcons.addDemoWifiView(state); + } + return view; + } + + @VisibleForTesting + protected StatusBarMobileView addMobileIcon(int index, String slot, MobileIconState state) { + StatusBarMobileView view = onCreateStatusBarMobileView(slot); + view.applyMobileState(state); + mGroup.addView(view, index, onCreateLayoutParams()); + + if (mIsInDemoMode) { + mDemoStatusIcons.addMobileView(state); + } + return view; + } + + private StatusBarIconView onCreateStatusBarIconView(String slot, boolean blocked) { return new StatusBarIconView(mContext, slot, null, blocked); } + private StatusBarWifiView onCreateStatusBarWifiView(String slot) { + StatusBarWifiView view = StatusBarWifiView.fromContext(mContext); + view.setSlot(slot); + return view; + } + + private StatusBarMobileView onCreateStatusBarMobileView(String slot) { + StatusBarMobileView view = StatusBarMobileView.fromContext(mContext); + view.setSlot(slot); + return view; + } + protected LinearLayout.LayoutParams onCreateLayoutParams() { return new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, mIconSize); } @@ -256,6 +334,9 @@ public interface StatusBarIconController { } protected void onRemoveIcon(int viewIndex) { + if (mIsInDemoMode) { + mDemoStatusIcons.onRemoveIcon((StatusIconDisplayable) mGroup.getChildAt(viewIndex)); + } mGroup.removeViewAt(viewIndex); } @@ -264,17 +345,59 @@ public interface StatusBarIconController { view.set(icon); } + public void onSetIconHolder(int viewIndex, StatusBarIconHolder holder) { + switch (holder.getType()) { + case TYPE_ICON: + onSetIcon(viewIndex, holder.getIcon()); + return; + case TYPE_WIFI: + onSetSignalIcon(viewIndex, holder.getWifiState()); + return; + + case TYPE_MOBILE: + onSetMobileIcon(viewIndex, holder.getMobileState()); + default: + break; + } + } + + public void onSetSignalIcon(int viewIndex, WifiIconState state) { + StatusBarWifiView wifiView = (StatusBarWifiView) mGroup.getChildAt(viewIndex); + if (wifiView != null) { + wifiView.applyWifiState(state); + } + + if (mIsInDemoMode) { + mDemoStatusIcons.updateWifiState(state); + } + } + + public void onSetMobileIcon(int viewIndex, MobileIconState state) { + StatusBarMobileView view = (StatusBarMobileView) mGroup.getChildAt(viewIndex); + if (view != null) { + view.applyMobileState(state); + } + + if (mIsInDemoMode) { + mDemoStatusIcons.updateMobileState(state); + } + } + @Override public void dispatchDemoCommand(String command, Bundle args) { if (!mDemoable) { return; } - if (mDemoStatusIcons != null && command.equals(COMMAND_EXIT)) { - mDemoStatusIcons.dispatchDemoCommand(command, args); - exitDemoMode(); + if (command.equals(COMMAND_EXIT)) { + if (mDemoStatusIcons != null) { + mDemoStatusIcons.dispatchDemoCommand(command, args); + exitDemoMode(); + } + mIsInDemoMode = false; } else { if (mDemoStatusIcons == null) { + mIsInDemoMode = true; mDemoStatusIcons = createDemoStatusIcons(); } mDemoStatusIcons.dispatchDemoCommand(command, args); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java index 8f5e705ff2a4..510af03e6f28 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java @@ -16,13 +16,14 @@ package com.android.systemui.statusbar.phone; +import android.annotation.NonNull; import android.content.Context; import android.graphics.drawable.Icon; import android.os.Bundle; import android.os.UserHandle; +import android.util.ArrayMap; import android.util.ArraySet; import android.view.ViewGroup; -import android.widget.LinearLayout; import com.android.internal.statusbar.StatusBarIcon; import com.android.systemui.Dependency; @@ -30,7 +31,9 @@ import com.android.systemui.Dumpable; import com.android.systemui.R; import com.android.systemui.SysUiServiceProvider; import com.android.systemui.statusbar.CommandQueue; -import com.android.systemui.statusbar.StatusBarIconView; +import com.android.systemui.statusbar.StatusIconDisplayable; +import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.MobileIconState; +import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.WifiIconState; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener; import com.android.systemui.statusbar.policy.IconLogger; @@ -40,8 +43,9 @@ import com.android.systemui.tuner.TunerService.Tunable; import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.ArrayList; +import java.util.List; -import static com.android.systemui.statusbar.phone.CollapsedStatusBarFragment.STATUS_BAR_ICON_MANAGER_TAG; +import static com.android.systemui.statusbar.phone.StatusBarIconController.TAG_PRIMARY; /** * Receives the callbacks from CommandQueue related to icons and tracks the state of @@ -50,20 +54,25 @@ import static com.android.systemui.statusbar.phone.CollapsedStatusBarFragment.ST */ public class StatusBarIconControllerImpl extends StatusBarIconList implements Tunable, ConfigurationListener, Dumpable, CommandQueue.Callbacks, StatusBarIconController { + private static final String TAG = "StatusBarIconController"; private final ArrayList<IconManager> mIconGroups = new ArrayList<>(); private final ArraySet<String> mIconBlacklist = new ArraySet<>(); private final IconLogger mIconLogger = Dependency.get(IconLogger.class); + // Points to light or dark context depending on the... context? private Context mContext; - private DemoStatusIcons mDemoStatusIcons; - private IconManager mStatusBarIconManager; + private Context mLightContext; + private Context mDarkContext; + + private boolean mIsDark = false; public StatusBarIconControllerImpl(Context context) { super(context.getResources().getStringArray( com.android.internal.R.array.config_statusBarIcons)); Dependency.get(ConfigurationController.class).addCallback(this); + mContext = context; loadDimens(); @@ -76,12 +85,16 @@ public class StatusBarIconControllerImpl extends StatusBarIconList implements Tu @Override public void addIconGroup(IconManager group) { mIconGroups.add(group); - for (int i = 0; i < mIcons.size(); i++) { - StatusBarIcon icon = mIcons.get(i); - if (icon != null) { - String slot = mSlots.get(i); - boolean blocked = mIconBlacklist.contains(slot); - group.onIconAdded(getViewIndex(getSlotIndex(slot)), slot, blocked, icon); + List<Slot> allSlots = getSlots(); + for (int i = 0; i < allSlots.size(); i++) { + Slot slot = allSlots.get(i); + List<StatusBarIconHolder> holders = slot.getHolderList(); + boolean blocked = mIconBlacklist.contains(slot.getName()); + + for (StatusBarIconHolder holder : holders) { + int tag = holder.getTag(); + int viewIndex = getViewIndex(getSlotIndex(slot.getName()), holder.getTag()); + group.onIconAdded(viewIndex, slot.getName(), blocked, holder); } } } @@ -99,104 +112,209 @@ public class StatusBarIconControllerImpl extends StatusBarIconList implements Tu } mIconBlacklist.clear(); mIconBlacklist.addAll(StatusBarIconController.getIconBlacklist(newValue)); - ArrayList<StatusBarIcon> current = new ArrayList<>(mIcons); - ArrayList<String> currentSlots = new ArrayList<>(mSlots); + ArrayList<Slot> currentSlots = getSlots(); + ArrayMap<Slot, List<StatusBarIconHolder>> slotsToReAdd = new ArrayMap<>(); + + // This is a little hacky... Peel off all of the holders on all of the slots + // but keep them around so they can be re-added + // Remove all the icons. - for (int i = current.size() - 1; i >= 0; i--) { - removeIcon(currentSlots.get(i)); + for (int i = currentSlots.size() - 1; i >= 0; i--) { + Slot s = currentSlots.get(i); + slotsToReAdd.put(s, s.getHolderList()); + removeAllIconsForSlot(s.getName()); } + // Add them all back - for (int i = 0; i < current.size(); i++) { - setIcon(currentSlots.get(i), current.get(i)); + for (int i = 0; i < currentSlots.size(); i++) { + Slot item = currentSlots.get(i); + List<StatusBarIconHolder> iconsForSlot = slotsToReAdd.get(item); + if (iconsForSlot == null) continue; + for (StatusBarIconHolder holder : iconsForSlot) { + setIcon(getSlotIndex(item.getName()), holder); + } } } private void loadDimens() { } - private void addSystemIcon(int index, StatusBarIcon icon) { - String slot = getSlot(index); - int viewIndex = getViewIndex(index); + private void addSystemIcon(int index, StatusBarIconHolder holder) { + String slot = getSlotName(index); + int viewIndex = getViewIndex(index, holder.getTag()); boolean blocked = mIconBlacklist.contains(slot); - mIconLogger.onIconVisibility(getSlot(index), icon.visible); - mIconGroups.forEach(l -> l.onIconAdded(viewIndex, slot, blocked, icon)); + mIconLogger.onIconVisibility(getSlotName(index), holder.isVisible()); + mIconGroups.forEach(l -> l.onIconAdded(viewIndex, slot, blocked, holder)); } @Override public void setIcon(String slot, int resourceId, CharSequence contentDescription) { int index = getSlotIndex(slot); - StatusBarIcon icon = getIcon(index); - if (icon == null) { - icon = new StatusBarIcon(UserHandle.SYSTEM, mContext.getPackageName(), - Icon.createWithResource(mContext, resourceId), 0, 0, contentDescription); - setIcon(slot, icon); + StatusBarIconHolder holder = getIcon(index, 0); + if (holder == null) { + StatusBarIcon icon = new StatusBarIcon(UserHandle.SYSTEM, mContext.getPackageName(), + Icon.createWithResource( + mContext, resourceId), 0, 0, contentDescription); + holder = StatusBarIconHolder.fromIcon(icon); + setIcon(index, holder); } else { - icon.icon = Icon.createWithResource(mContext, resourceId); - icon.contentDescription = contentDescription; - handleSet(index, icon); + holder.getIcon().icon = Icon.createWithResource(mContext, resourceId); + holder.getIcon().contentDescription = contentDescription; + handleSet(index, holder); + } + } + + /** + * Signal icons need to be handled differently, because they can be + * composite views + */ + @Override + public void setSignalIcon(String slot, WifiIconState state) { + + int index = getSlotIndex(slot); + + if (state == null) { + removeIcon(index, 0); + return; + } + + StatusBarIconHolder holder = getIcon(index, 0); + if (holder == null) { + holder = StatusBarIconHolder.fromWifiIconState(state); + setIcon(index, holder); + } else { + holder.setWifiState(state); + handleSet(index, holder); + } + } + + /** + * Accept a list of MobileIconStates, which all live in the same slot(?!), and then are sorted + * by subId. Don't worry this definitely makes sense and works. + * @param slot da slot + * @param iconStates All of the mobile icon states + */ + @Override + public void setMobileIcons(String slot, List<MobileIconState> iconStates) { + Slot mobileSlot = getSlot(slot); + int slotIndex = getSlotIndex(slot); + + for (MobileIconState state : iconStates) { + StatusBarIconHolder holder = mobileSlot.getHolderForTag(state.subId); + if (holder == null) { + holder = StatusBarIconHolder.fromMobileIconState(state); + setIcon(slotIndex, holder); + } else { + holder.setMobileState(state); + handleSet(slotIndex, holder); + } } } @Override public void setExternalIcon(String slot) { - int viewIndex = getViewIndex(getSlotIndex(slot)); + int viewIndex = getViewIndex(getSlotIndex(slot), 0); int height = mContext.getResources().getDimensionPixelSize( R.dimen.status_bar_icon_drawing_size); mIconGroups.forEach(l -> l.onIconExternal(viewIndex, height)); } + //TODO: remove this (used in command queue and for 3rd party tiles?) @Override public void setIcon(String slot, StatusBarIcon icon) { setIcon(getSlotIndex(slot), icon); } + /** + * For backwards compatibility, in the event that someone gives us a slot and a status bar icon + */ + private void setIcon(int index, StatusBarIcon icon) { + if (icon == null) { + removeAllIconsForSlot(getSlotName(index)); + return; + } + + StatusBarIconHolder holder = StatusBarIconHolder.fromIcon(icon); + setIcon(index, holder); + } + @Override - public void removeIcon(String slot) { - int index = getSlotIndex(slot); - removeIcon(index); + public void setIcon(int index, @NonNull StatusBarIconHolder holder) { + boolean isNew = getIcon(index, holder.getTag()) == null; + super.setIcon(index, holder); + + if (isNew) { + addSystemIcon(index, holder); + } else { + handleSet(index, holder); + } } public void setIconVisibility(String slot, boolean visibility) { int index = getSlotIndex(slot); - StatusBarIcon icon = getIcon(index); - if (icon == null || icon.visible == visibility) { + StatusBarIconHolder holder = getIcon(index, 0); + if (holder == null || holder.isVisible() == visibility) { return; } - icon.visible = visibility; - handleSet(index, icon); + + holder.setVisible(visibility); + handleSet(index, holder); + } + + public void removeIcon(String slot) { + removeAllIconsForSlot(slot); + } + + @Override + public void removeIcon(String slot, int tag) { + removeIcon(getSlotIndex(slot), tag); } @Override - public void removeIcon(int index) { - if (getIcon(index) == null) { + public void removeAllIconsForSlot(String slotName) { + Slot slot = getSlot(slotName); + if (!slot.hasIconsInSlot()) { return; } - mIconLogger.onIconHidden(getSlot(index)); - super.removeIcon(index); - int viewIndex = getViewIndex(index); - mIconGroups.forEach(l -> l.onRemoveIcon(viewIndex)); + + mIconLogger.onIconHidden(slotName); + + int slotIndex = getSlotIndex(slotName); + List<StatusBarIconHolder> iconsToRemove = slot.getHolderList(); + for (StatusBarIconHolder holder : iconsToRemove) { + int viewIndex = getViewIndex(slotIndex, holder.getTag()); + slot.removeForTag(holder.getTag()); + mIconGroups.forEach(l -> l.onRemoveIcon(viewIndex)); + } } @Override - public void setIcon(int index, StatusBarIcon icon) { - if (icon == null) { - removeIcon(index); + public void removeIcon(int index, int tag) { + if (getIcon(index, tag) == null) { return; } - boolean isNew = getIcon(index) == null; - super.setIcon(index, icon); - if (isNew) { - addSystemIcon(index, icon); - } else { - handleSet(index, icon); - } + mIconLogger.onIconHidden(getSlotName(index)); + super.removeIcon(index, tag); + int viewIndex = getViewIndex(index, 0); + mIconGroups.forEach(l -> l.onRemoveIcon(viewIndex)); } - private void handleSet(int index, StatusBarIcon icon) { - int viewIndex = getViewIndex(index); - mIconLogger.onIconVisibility(getSlot(index), icon.visible); - mIconGroups.forEach(l -> l.onSetIcon(viewIndex, icon)); + private void handleSet(int index, StatusBarIconHolder holder) { + int viewIndex = getViewIndex(index, holder.getTag()); + mIconLogger.onIconVisibility(getSlotName(index), holder.isVisible()); + mIconGroups.forEach(l -> l.onSetIconHolder(viewIndex, holder)); + } + + /** + * For mobile essentially (an array of holders in one slot) + */ + private void handleSet(int slotIndex, List<StatusBarIconHolder> holders) { + for (StatusBarIconHolder holder : holders) { + int viewIndex = getViewIndex(slotIndex, holder.getTag()); + mIconLogger.onIconVisibility(getSlotName(slotIndex), holder.isVisible()); + mIconGroups.forEach(l -> l.onSetIconHolder(viewIndex, holder)); + } } @Override @@ -208,7 +326,7 @@ public class StatusBarIconControllerImpl extends StatusBarIconList implements Tu int N = group.getChildCount(); pw.println(" icon views: " + N); for (int i = 0; i < N; i++) { - StatusBarIconView ic = (StatusBarIconView) group.getChildAt(i); + StatusIconDisplayable ic = (StatusIconDisplayable) group.getChildAt(i); pw.println(" [" + i + "] icon=" + ic); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconHolder.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconHolder.java new file mode 100644 index 000000000000..e854dd0cd737 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconHolder.java @@ -0,0 +1,131 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.phone; + +import android.annotation.Nullable; +import android.content.Context; +import android.graphics.drawable.Icon; +import android.os.UserHandle; +import com.android.internal.statusbar.StatusBarIcon; +import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.MobileIconState; +import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.WifiIconState; + +/** + * Wraps {@link com.android.internal.statusbar.StatusBarIcon} so we can still have a uniform list + */ +public class StatusBarIconHolder { + public static final int TYPE_ICON = 0; + public static final int TYPE_WIFI = 1; + public static final int TYPE_MOBILE = 2; + + private StatusBarIcon mIcon; + private WifiIconState mWifiState; + private MobileIconState mMobileState; + private int mType = TYPE_ICON; + private int mTag = 0; + private boolean mVisible = true; + + public static StatusBarIconHolder fromIcon(StatusBarIcon icon) { + StatusBarIconHolder wrapper = new StatusBarIconHolder(); + wrapper.mIcon = icon; + + return wrapper; + } + + public static StatusBarIconHolder fromResId(Context context, int resId, + CharSequence contentDescription) { + StatusBarIconHolder holder = new StatusBarIconHolder(); + holder.mIcon = new StatusBarIcon(UserHandle.SYSTEM, context.getPackageName(), + Icon.createWithResource( context, resId), 0, 0, contentDescription); + return holder; + } + + public static StatusBarIconHolder fromWifiIconState(WifiIconState state) { + StatusBarIconHolder holder = new StatusBarIconHolder(); + holder.mWifiState = state; + holder.mType = TYPE_WIFI; + return holder; + } + + public static StatusBarIconHolder fromMobileIconState(MobileIconState state) { + StatusBarIconHolder holder = new StatusBarIconHolder(); + holder.mMobileState = state; + holder.mType = TYPE_MOBILE; + holder.mTag = state.subId; + return holder; + } + + public int getType() { + return mType; + } + + @Nullable + public StatusBarIcon getIcon() { + return mIcon; + } + + @Nullable + public WifiIconState getWifiState() { + return mWifiState; + } + + public void setWifiState(WifiIconState state) { + mWifiState = state; + } + + @Nullable + public MobileIconState getMobileState() { + return mMobileState; + } + + public void setMobileState(MobileIconState state) { + mMobileState = state; + } + + public boolean isVisible() { + switch (mType) { + case TYPE_ICON: + return mIcon.visible; + case TYPE_WIFI: + return mWifiState.visible; + case TYPE_MOBILE: + return mMobileState.visible; + + default: return true; + } + } + + public void setVisible(boolean visible) { + switch (mType) { + case TYPE_ICON: + mIcon.visible = visible; + break; + + case TYPE_WIFI: + mWifiState.visible = visible; + break; + + case TYPE_MOBILE: + mMobileState.visible = visible; + break; + } + } + + public int getTag() { + return mTag; + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconList.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconList.java index 1aa3a4312f8d..c773170a2756 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconList.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconList.java @@ -16,64 +16,78 @@ package com.android.systemui.statusbar.phone; +import android.annotation.NonNull; +import android.annotation.Nullable; +import com.android.internal.annotations.VisibleForTesting; import com.android.internal.statusbar.StatusBarIcon; - import java.io.PrintWriter; import java.util.ArrayList; +import java.util.List; + +import static com.android.systemui.statusbar.phone.StatusBarIconController.TAG_PRIMARY; public class StatusBarIconList { - protected ArrayList<String> mSlots = new ArrayList<>(); - protected ArrayList<StatusBarIcon> mIcons = new ArrayList<>(); + private ArrayList<Slot> mSlots = new ArrayList<>(); public StatusBarIconList(String[] slots) { final int N = slots.length; for (int i=0; i < N; i++) { - mSlots.add(slots[i]); - mIcons.add(null); + mSlots.add(new Slot(slots[i], null)); } } public int getSlotIndex(String slot) { final int N = mSlots.size(); for (int i=0; i<N; i++) { - if (slot.equals(mSlots.get(i))) { + Slot item = mSlots.get(i); + if (item.getName().equals(slot)) { return i; } } // Auto insert new items at the beginning. - mSlots.add(0, slot); - mIcons.add(0, null); + mSlots.add(0, new Slot(slot, null)); return 0; } + protected ArrayList<Slot> getSlots() { + return new ArrayList<>(mSlots); + } + + protected Slot getSlot(String name) { + return mSlots.get(getSlotIndex(name)); + } + public int size() { return mSlots.size(); } - public void setIcon(int index, StatusBarIcon icon) { - mIcons.set(index, icon); + public void setIcon(int index, @NonNull StatusBarIconHolder holder) { + mSlots.get(index).addHolder(holder); } - public void removeIcon(int index) { - mIcons.set(index, null); + public void removeIcon(int index, int tag) { + mSlots.get(index).removeForTag(tag); } - public String getSlot(int index) { - return mSlots.get(index); + public String getSlotName(int index) { + return mSlots.get(index).getName(); } - public StatusBarIcon getIcon(int index) { - return mIcons.get(index); + public StatusBarIconHolder getIcon(int index, int tag) { + return mSlots.get(index).getHolderForTag(tag); } - public int getViewIndex(int index) { + public int getViewIndex(int slotIndex, int tag) { int count = 0; - for (int i = 0; i < index; i++) { - if (mIcons.get(i) != null) { - count++; + for (int i = 0; i < slotIndex; i++) { + Slot item = mSlots.get(i); + if (item.hasIconsInSlot()) { + count += item.numberOfIcons(); } } - return count; + + Slot viewItem = mSlots.get(slotIndex); + return count + viewItem.viewIndexOffsetForTag(tag); } public void dump(PrintWriter pw) { @@ -81,7 +95,163 @@ public class StatusBarIconList { final int N = mSlots.size(); pw.println(" icon slots: " + N); for (int i=0; i<N; i++) { - pw.printf(" %2d: (%s) %s\n", i, mSlots.get(i), mIcons.get(i)); + pw.printf(" %2d:%s\n", i, mSlots.get(i).toString()); + } + } + + public static class Slot { + private final String mName; + private StatusBarIconHolder mHolder; + /** + * Only used if multiple icons are added to the same slot. + * + * If there are mSubSlots, then these are structured like: + * [ First item | (the rest) ] + * + * The tricky thing to keep in mind here is that the list [mHolder, mSubSlots] is ordered + * ascending, but for view logic we should go backwards through the list. I.e., the first + * element (mHolder) should be the highest index, because higher priority items go to the + * right of lower priority items + */ + private ArrayList<StatusBarIconHolder> mSubSlots; + + public Slot(String name, StatusBarIconHolder iconHolder) { + mName = name; + mHolder = iconHolder; + } + + public String getName() { + return mName; + } + + @Nullable + public StatusBarIconHolder getHolderForTag(int tag) { + if (tag == TAG_PRIMARY) { + return mHolder; + } + + if (mSubSlots != null) { + for (StatusBarIconHolder holder : mSubSlots) { + if (holder.getTag() == tag) { + return holder; + } + } + } + + return null; + } + + public void addHolder(StatusBarIconHolder holder) { + int tag = holder.getTag(); + if (tag == TAG_PRIMARY) { + mHolder = holder; + } else { + setSubSlot(holder, tag); + } + } + + public void removeForTag(int tag) { + if (tag == TAG_PRIMARY) { + mHolder = null; + } else { + int index = getIndexForTag(tag); + if (index != -1) { + mSubSlots.remove(index); + } + } + } + + @VisibleForTesting + public void clear() { + mHolder = null; + if (mSubSlots != null) { + mSubSlots = null; + } + } + + private void setSubSlot(StatusBarIconHolder holder, int tag) { + if (mSubSlots == null) { + mSubSlots = new ArrayList<>(); + mSubSlots.add(holder); + return; + } + + if (getIndexForTag(tag) != -1) { + // Holder exists for tag; no-op + return; + } + + // These holders get added to the end. Confused yet? + mSubSlots.add(holder); + } + + private int getIndexForTag(int tag) { + for (int i = 0; i < mSubSlots.size(); i++) { + StatusBarIconHolder h = mSubSlots.get(i); + if (h.getTag() == tag) { + return i; + } + } + + return -1; + } + + public boolean hasIconsInSlot() { + if (mHolder != null) return true; + if (mSubSlots == null) return false; + + return mSubSlots.size() > 0; + } + + public int numberOfIcons() { + int num = mHolder == null ? 0 : 1; + if (mSubSlots == null) return num; + + return num + mSubSlots.size(); + } + + /** + * View index is backwards from regular index + * @param tag the tag of the holder being viewed + * @return (1 + mSubSlots.size() - indexOfTag) + */ + public int viewIndexOffsetForTag(int tag) { + if (mSubSlots == null) { + return 0; + } + + int subSlots = mSubSlots.size(); + if (tag == TAG_PRIMARY) { + return subSlots; + } + + return subSlots - getIndexForTag(tag) - 1; + } + + public List<StatusBarIconHolder> getHolderList() { + ArrayList<StatusBarIconHolder> holders = new ArrayList<>(); + if (mHolder != null) { + holders.add(mHolder); + } + + if (mSubSlots != null) { + holders.addAll(mSubSlots); + } + + return holders; + } + + @Override + public String toString() { + return String.format("(%s) %s", mName, subSlotsString()); + } + + private String subSlotsString() { + if (mSubSlots == null) { + return ""; + } + + return "" + mSubSlots.size() + " subSlots"; } } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java new file mode 100644 index 000000000000..c5a3a0d33cd3 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java @@ -0,0 +1,420 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.phone; + +import android.content.Context; +import android.content.res.ColorStateList; +import android.graphics.Rect; +import android.os.Handler; +import android.os.Looper; +import android.telephony.SubscriptionInfo; +import android.util.ArraySet; +import android.util.Log; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.view.accessibility.AccessibilityEvent; +import android.widget.ImageView; +import com.android.settingslib.graph.SignalDrawable; +import com.android.systemui.Dependency; +import com.android.systemui.R; +import com.android.systemui.statusbar.phone.StatusBarIconController; +import com.android.systemui.statusbar.policy.DarkIconDispatcher; +import com.android.systemui.statusbar.policy.NetworkController; +import com.android.systemui.statusbar.policy.NetworkController.IconState; +import com.android.systemui.statusbar.policy.NetworkControllerImpl; +import com.android.systemui.statusbar.policy.SecurityController; +import com.android.systemui.tuner.TunerService.Tunable; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + + +public class StatusBarSignalPolicy implements NetworkControllerImpl.SignalCallback, + SecurityController.SecurityControllerCallback, Tunable { + private static final String TAG = "StatusBarSignalPolicy"; + + private final String mSlotAirplane; + private final String mSlotMobile; + private final String mSlotWifi; + private final String mSlotEthernet; + private final String mSlotVpn; + + private final Context mContext; + private final StatusBarIconController mIconController; + private final NetworkController mNetworkController; + private final SecurityController mSecurityController; + private final Handler mHandler = Handler.getMain(); + + private boolean mBlockAirplane; + private boolean mBlockMobile; + private boolean mBlockWifi; + private boolean mBlockEthernet; + private boolean mActivityEnabled; + private boolean mForceBlockWifi; + + // Track as little state as possible, and only for padding purposes + private boolean mIsAirplaneMode = false; + private boolean mWifiVisible = false; + + private ArrayList<MobileIconState> mMobileStates = new ArrayList<MobileIconState>(); + private WifiIconState mWifiIconState = new WifiIconState(); + + public StatusBarSignalPolicy(Context context, StatusBarIconController iconController) { + mContext = context; + + mSlotAirplane = mContext.getString(com.android.internal.R.string.status_bar_airplane); + mSlotMobile = mContext.getString(com.android.internal.R.string.status_bar_mobile); + mSlotWifi = mContext.getString(com.android.internal.R.string.status_bar_wifi); + mSlotEthernet = mContext.getString(com.android.internal.R.string.status_bar_ethernet); + mSlotVpn = mContext.getString(com.android.internal.R.string.status_bar_vpn); + + mIconController = iconController; + mNetworkController = Dependency.get(NetworkController.class); + mSecurityController = Dependency.get(SecurityController.class); + + mNetworkController.addCallback(this); + mSecurityController.addCallback(this); + } + + public void destroy() { + mNetworkController.removeCallback(this); + mSecurityController.removeCallback(this); + } + + private void updateVpn() { + boolean vpnVisible = mSecurityController.isVpnEnabled(); + int vpnIconId = currentVpnIconId(mSecurityController.isVpnBranded()); + + mIconController.setIcon(mSlotVpn, vpnIconId, null); + mIconController.setIconVisibility(mSlotVpn, vpnVisible); + } + + private int currentVpnIconId(boolean isBranded) { + return isBranded ? R.drawable.stat_sys_branded_vpn : R.drawable.stat_sys_vpn_ic; + } + + private void updateActivityEnabled() { + mActivityEnabled = mContext.getResources().getBoolean(R.bool.config_showActivity); + } + + /** + * From SecurityController + */ + @Override + public void onStateChanged() { + mHandler.post(this::updateVpn); + } + + @Override + public void onTuningChanged(String key, String newValue) { + if (!StatusBarIconController.ICON_BLACKLIST.equals(key)) { + return; + } + ArraySet<String> blockList = StatusBarIconController.getIconBlacklist(newValue); + boolean blockAirplane = blockList.contains(mSlotAirplane); + boolean blockMobile = blockList.contains(mSlotMobile); + boolean blockWifi = blockList.contains(mSlotWifi); + boolean blockEthernet = blockList.contains(mSlotEthernet); + + if (blockAirplane != mBlockAirplane || blockMobile != mBlockMobile + || blockEthernet != mBlockEthernet || blockWifi != mBlockWifi) { + mBlockAirplane = blockAirplane; + mBlockMobile = blockMobile; + mBlockEthernet = blockEthernet; + mBlockWifi = blockWifi || mForceBlockWifi; + // Re-register to get new callbacks. + mNetworkController.removeCallback(this); + } + } + + @Override + public void setWifiIndicators(boolean enabled, IconState statusIcon, IconState qsIcon, + boolean activityIn, boolean activityOut, String description, boolean isTransient, + String statusLabel) { + + boolean visible = statusIcon.visible && !mBlockWifi; + boolean in = activityIn && mActivityEnabled && visible; + boolean out = activityOut && mActivityEnabled && visible; + + mWifiIconState.visible = visible; + mWifiIconState.resId = statusIcon.icon; + mWifiIconState.activityIn = in; + mWifiIconState.activityOut = out; + mWifiIconState.slot = mSlotWifi; + mWifiIconState.airplaneSpacerVisible = mIsAirplaneMode; + mWifiIconState.contentDescription = statusIcon.contentDescription; + + if (mWifiIconState.visible && mWifiIconState.resId > 0) { + mIconController.setSignalIcon(mSlotWifi, mWifiIconState.copy()); + mIconController.setIconVisibility(mSlotWifi, true); + } else { + mIconController.setIconVisibility(mSlotWifi, false); + } + } + + @Override + public void setMobileDataIndicators(IconState statusIcon, IconState qsIcon, int statusType, + int qsType, boolean activityIn, boolean activityOut, String typeContentDescription, + String description, boolean isWide, int subId, boolean roaming) { + MobileIconState state = getState(subId); + if (state == null) { + return; + } + + state.visible = statusIcon.visible && !mBlockMobile; + state.strengthId = statusIcon.icon; + state.typeId = statusType; + state.contentDescription = statusIcon.contentDescription; + state.typeContentDescription = typeContentDescription; + state.roaming = roaming; + state.activityIn = activityIn && mActivityEnabled; + state.activityOut = activityOut && mActivityEnabled; + + // Always send a copy to maintain value type semantics + mIconController.setMobileIcons(mSlotMobile, MobileIconState.copyStates(mMobileStates)); + } + + private MobileIconState getState(int subId) { + for (MobileIconState state : mMobileStates) { + if (state.subId == subId) { + return state; + } + } + Log.e(TAG, "Unexpected subscription " + subId); + return null; + } + + + /** + * It is expected that a call to setSubs will be immediately followed by setMobileDataIndicators + * so we don't have to update the icon manager at this point, just remove the old ones + * @param subs list of mobile subscriptions, displayed as mobile data indicators (max 8) + */ + @Override + public void setSubs(List<SubscriptionInfo> subs) { + if (hasCorrectSubs(subs)) { + return; + } + + mIconController.removeAllIconsForSlot(mSlotMobile); + mMobileStates.clear(); + final int n = subs.size(); + for (int i = 0; i < n; i++) { + mMobileStates.add(new MobileIconState(subs.get(i).getSubscriptionId())); + } + } + + private boolean hasCorrectSubs(List<SubscriptionInfo> subs) { + final int N = subs.size(); + if (N != mMobileStates.size()) { + return false; + } + for (int i = 0; i < N; i++) { + if (mMobileStates.get(i).subId != subs.get(i).getSubscriptionId()) { + return false; + } + } + return true; + } + + @Override + public void setNoSims(boolean show, boolean simDetected) { + // Noop yay! + } + + + @Override + public void setEthernetIndicators(IconState state) { + boolean visible = state.visible && !mBlockEthernet; + int resId = state.icon; + String description = state.contentDescription; + + if (resId > 0) { + mIconController.setIcon(mSlotEthernet, resId, description); + mIconController.setIconVisibility(mSlotEthernet, true); + } else { + mIconController.setIconVisibility(mSlotEthernet, false); + } + } + + @Override + public void setIsAirplaneMode(IconState icon) { + mIsAirplaneMode = icon.visible && !mBlockAirplane; + int resId = icon.icon; + String description = icon.contentDescription; + + if (mIsAirplaneMode && resId > 0) { + mIconController.setIcon(mSlotAirplane, resId, description); + mIconController.setIconVisibility(mSlotAirplane, true); + } else { + mIconController.setIconVisibility(mSlotAirplane, false); + } + } + + @Override + public void setMobileDataEnabled(boolean enabled) { + // Don't care. + } + + private static abstract class SignalIconState { + public boolean visible; + public boolean activityOut; + public boolean activityIn; + public String slot; + public String contentDescription; + + @Override + public boolean equals(Object o) { + // Skipping reference equality bc this should be more of a value type + if (o == null || getClass() != o.getClass()) { + return false; + } + SignalIconState that = (SignalIconState) o; + return visible == that.visible && + activityOut == that.activityOut && + activityIn == that.activityIn && + Objects.equals(contentDescription, that.contentDescription) && + Objects.equals(slot, that.slot); + } + + @Override + public int hashCode() { + return Objects.hash(visible, activityOut, slot); + } + + protected void copyTo(SignalIconState other) { + other.visible = visible; + other.activityIn = activityIn; + other.activityOut = activityOut; + other.slot = slot; + other.contentDescription = contentDescription; + } + } + + public static class WifiIconState extends SignalIconState{ + public int resId; + public boolean airplaneSpacerVisible; + public boolean signalSpacerVisible; + + @Override + public boolean equals(Object o) { + // Skipping reference equality bc this should be more of a value type + if (o == null || getClass() != o.getClass()) { + return false; + } + if (!super.equals(o)) { + return false; + } + WifiIconState that = (WifiIconState) o; + return resId == that.resId && + airplaneSpacerVisible == that.airplaneSpacerVisible && + signalSpacerVisible == that.signalSpacerVisible; + } + + public void copyTo(WifiIconState other) { + super.copyTo(other); + other.resId = resId; + other.airplaneSpacerVisible = airplaneSpacerVisible; + other.signalSpacerVisible = signalSpacerVisible; + } + + public WifiIconState copy() { + WifiIconState newState = new WifiIconState(); + copyTo(newState); + return newState; + } + + @Override + public int hashCode() { + return Objects.hash(super.hashCode(), + resId, airplaneSpacerVisible, signalSpacerVisible); + } + + @Override public String toString() { + return "WifiIconState(resId=" + resId + ", visible=" + visible + ")"; + } + } + + /** + * A little different. This one delegates to SignalDrawable instead of a specific resId + */ + public static class MobileIconState extends SignalIconState { + public int subId; + public int strengthId; + public int typeId; + public boolean roaming; + public boolean needsLeadingPadding; + public String typeContentDescription; + + private MobileIconState(int subId) { + super(); + this.subId = subId; + } + + @Override + public boolean equals(Object o) { + if (o == null || getClass() != o.getClass()) { + return false; + } + if (!super.equals(o)) { + return false; + } + MobileIconState that = (MobileIconState) o; + return subId == that.subId && + strengthId == that.strengthId && + typeId == that.typeId && + roaming == that.roaming && + needsLeadingPadding == that.needsLeadingPadding && + Objects.equals(typeContentDescription, that.typeContentDescription); + } + + @Override + public int hashCode() { + + return Objects + .hash(super.hashCode(), subId, strengthId, typeId, roaming, needsLeadingPadding, + typeContentDescription); + } + + public void copyTo(MobileIconState other) { + super.copyTo(other); + other.subId = subId; + other.strengthId = strengthId; + other.typeId = typeId; + other.roaming = roaming; + other.needsLeadingPadding = needsLeadingPadding; + other.typeContentDescription = typeContentDescription; + } + + private static List<MobileIconState> copyStates(List<MobileIconState> inStates) { + ArrayList<MobileIconState> outStates = new ArrayList<>(); + for (MobileIconState state : inStates) { + MobileIconState copy = new MobileIconState(state.subId); + state.copyTo(copy); + outStates.add(copy); + } + + return outStates; + } + + @Override public String toString() { + return "MobileIconState(subId=" + subId + ", strengthId=" + strengthId + ", roaming=" + + roaming + ", visible=" + visible + ")"; + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusIconContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusIconContainer.java index dab28d65a5a9..255e10e2680d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusIconContainer.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusIconContainer.java @@ -18,13 +18,13 @@ package com.android.systemui.statusbar.phone; import android.annotation.Nullable; import android.content.Context; -import android.util.ArrayMap; import android.util.AttributeSet; +import android.util.Log; import android.view.View; import com.android.keyguard.AlphaOptimizedLinearLayout; import com.android.systemui.R; -import com.android.systemui.statusbar.StatusBarIconView; +import com.android.systemui.statusbar.StatusIconDisplayable; import com.android.systemui.statusbar.stack.ViewState; /** @@ -97,7 +97,7 @@ public class StatusIconContainer extends AlphaOptimizedLinearLayout { * Layout is happening from end -> start */ private void calculateIconTranslations() { - float width = getWidth(); + float width = getWidth() - getPaddingEnd(); float translationX = width; float contentStart = getPaddingStart(); int childCount = getChildCount(); @@ -109,18 +109,22 @@ public class StatusIconContainer extends AlphaOptimizedLinearLayout { //TODO: Dots for (int i = childCount - 1; i >= 0; i--) { View child = getChildAt(i); - if (!(child instanceof StatusBarIconView)) { + if (!(child instanceof StatusIconDisplayable)) { + if (DEBUG) Log.d(TAG, "skipping child (wrong type)"); continue; } + StatusIconDisplayable iconView = (StatusIconDisplayable) child; + ViewState childState = getViewStateFromChild(child); if (childState == null ) { + if (DEBUG) Log.d(TAG, "skipping child (" + iconView.getSlot() + ") no ViewState"); continue; } - // Rely on StatusBarIcon for truth about visibility - if (!((StatusBarIconView) child).getStatusBarIcon().visible) { + if (!iconView.isIconVisible() || iconView.isIconBlocked()) { childState.hidden = true; + if (DEBUG) Log.d(TAG, "skipping child (" + iconView.getSlot() + ") not visible"); continue; } @@ -175,8 +179,8 @@ public class StatusIconContainer extends AlphaOptimizedLinearLayout { vs.initFrom(child); vs.alpha = 1.0f; - if (child instanceof StatusBarIconView) { - vs.hidden = !((StatusBarIconView)child).getStatusBarIcon().visible; + if (child instanceof StatusIconDisplayable) { + vs.hidden = !((StatusIconDisplayable)child).isIconVisible(); } else { vs.hidden = false; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DarkIconDispatcher.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DarkIconDispatcher.java index 58944c6ad571..945ed761c2b4 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DarkIconDispatcher.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DarkIconDispatcher.java @@ -35,7 +35,7 @@ public interface DarkIconDispatcher { // Used to reapply darkness on an object, must have previously been added through // addDarkReceiver. - void applyDark(ImageView object); + void applyDark(DarkReceiver object); int DEFAULT_ICON_TINT = Color.WHITE; Rect sTmpRect = new Rect(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java index 4533aa5e2f76..4c100cdb99ff 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java @@ -54,6 +54,7 @@ import com.android.systemui.R; import com.android.systemui.settings.CurrentUserTracker; import com.android.systemui.statusbar.policy.DeviceProvisionedController.DeviceProvisionedListener; +import com.android.systemui.statusbar.policy.MobileSignalController.MobileState; import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.ArrayList; @@ -848,6 +849,11 @@ public class NetworkControllerImpl extends BroadcastReceiver subs.add(addSignalController(i, i)); } mCallbackHandler.setSubs(subs); + for (int i = 0; i < mMobileSignalControllers.size(); i++) { + int key = mMobileSignalControllers.keyAt(i); + MobileSignalController controller = mMobileSignalControllers.get(key); + controller.notifyListeners(); + } } } String nosim = args.getString("nosim"); diff --git a/packages/SystemUI/src/com/android/systemui/util/NotificationChannels.java b/packages/SystemUI/src/com/android/systemui/util/NotificationChannels.java index 1cbdfe81869d..7a9cdfd85645 100644 --- a/packages/SystemUI/src/com/android/systemui/util/NotificationChannels.java +++ b/packages/SystemUI/src/com/android/systemui/util/NotificationChannels.java @@ -39,8 +39,7 @@ public class NotificationChannels extends SystemUI { public static String BATTERY = "BAT"; public static String HINTS = "HNT"; - @VisibleForTesting - static void createAll(Context context) { + public static void createAll(Context context) { final NotificationManager nm = context.getSystemService(NotificationManager.class); final NotificationChannel batteryChannel = new NotificationChannel(BATTERY, context.getString(R.string.notification_channel_battery), diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java index acdd0c71b491..6f71e55b0d3c 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java +++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java @@ -603,7 +603,8 @@ public class VolumeDialogImpl implements VolumeDialog { if (row.defaultStream) { return activeRow.stream == STREAM_RING || activeRow.stream == STREAM_ALARM - || activeRow.stream == STREAM_VOICE_CALL; + || activeRow.stream == STREAM_VOICE_CALL + || activeRow.stream == STREAM_ACCESSIBILITY; } return false; diff --git a/packages/SystemUI/tests/src/com/android/systemui/power/PowerUITest.java b/packages/SystemUI/tests/src/com/android/systemui/power/PowerUITest.java index 4b455baee56b..149f2de06be4 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/power/PowerUITest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/power/PowerUITest.java @@ -58,6 +58,7 @@ public class PowerUITest extends SysuiTestCase { public static final int BELOW_WARNING_BUCKET = -1; public static final long BELOW_HYBRID_THRESHOLD = TimeUnit.HOURS.toMillis(2); public static final long ABOVE_HYBRID_THRESHOLD = TimeUnit.HOURS.toMillis(4); + private static final long ABOVE_CHARGE_CYCLE_THRESHOLD = Duration.ofHours(8).toMillis(); private HardwarePropertiesManager mHardProps; private WarningsUI mMockWarnings; private PowerUI mPowerUI; @@ -198,6 +199,7 @@ public class PowerUITest extends SysuiTestCase { when(mEnhancedEstimates.isHybridNotificationEnabled()).thenReturn(true); when(mEnhancedEstimates.getLowWarningThreshold()).thenReturn(PowerUI.THREE_HOURS_IN_MILLIS); when(mEnhancedEstimates.getSevereWarningThreshold()).thenReturn(ONE_HOUR_MILLIS); + mPowerUI.mBatteryLevel = 10; mPowerUI.start(); // unplugged device that would show the non-hybrid notification and the hybrid @@ -213,6 +215,7 @@ public class PowerUITest extends SysuiTestCase { when(mEnhancedEstimates.isHybridNotificationEnabled()).thenReturn(true); when(mEnhancedEstimates.getLowWarningThreshold()).thenReturn(PowerUI.THREE_HOURS_IN_MILLIS); when(mEnhancedEstimates.getSevereWarningThreshold()).thenReturn(ONE_HOUR_MILLIS); + mPowerUI.mBatteryLevel = 10; mPowerUI.start(); // unplugged device that would show the non-hybrid but not the hybrid @@ -254,13 +257,14 @@ public class PowerUITest extends SysuiTestCase { } @Test - public void testShouldShowLowBatteryWarning_deviceBatteryStatusUnkown_returnsNoShow() { + public void testShouldShowLowBatteryWarning_deviceBatteryStatusUnknown_returnsNoShow() { when(mEnhancedEstimates.isHybridNotificationEnabled()).thenReturn(true); when(mEnhancedEstimates.getLowWarningThreshold()).thenReturn(PowerUI.THREE_HOURS_IN_MILLIS); when(mEnhancedEstimates.getSevereWarningThreshold()).thenReturn(ONE_HOUR_MILLIS); mPowerUI.start(); - // Unknown battery status device that would show the neither due + // Unknown battery status device that would show the neither due to the battery status being + // unknown boolean shouldShow = mPowerUI.shouldShowLowBatteryWarning(UNPLUGGED, UNPLUGGED, ABOVE_WARNING_BUCKET, BELOW_WARNING_BUCKET, BELOW_HYBRID_THRESHOLD, @@ -295,6 +299,9 @@ public class PowerUITest extends SysuiTestCase { mPowerUI.maybeShowBatteryWarning(UNPLUGGED, UNPLUGGED, ABOVE_WARNING_BUCKET, ABOVE_WARNING_BUCKET); + + // reduce battery level to handle time based trigger -> level trigger interactions + mPowerUI.mBatteryLevel = 10; boolean shouldShow = mPowerUI.shouldShowLowBatteryWarning(UNPLUGGED, UNPLUGGED, ABOVE_WARNING_BUCKET, ABOVE_WARNING_BUCKET, BELOW_HYBRID_THRESHOLD, diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/ExpandableNotificationRowTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/ExpandableNotificationRowTest.java index 23a34051bd7a..ab042d4ce491 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/ExpandableNotificationRowTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/ExpandableNotificationRowTest.java @@ -16,6 +16,8 @@ package com.android.systemui.statusbar; +import static android.app.NotificationManager.IMPORTANCE_DEFAULT; +import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; @@ -29,6 +31,7 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.app.AppOpsManager; +import android.app.NotificationChannel; import android.support.test.filters.SmallTest; import android.support.test.runner.AndroidJUnit4; import android.util.ArraySet; @@ -50,6 +53,7 @@ import org.mockito.Spy; import org.mockito.junit.MockitoJUnit; import org.mockito.junit.MockitoRule; +import java.util.List; import java.util.function.Consumer; @SmallTest @@ -274,4 +278,24 @@ public class ExpandableNotificationRowTest extends SysuiTestCase { mGroupRow.setBlockingHelperShowing(false); assertFalse(mGroupRow.isBlockingHelperShowing()); } + + @Test + public void testGetNumUniqueChildren_defaultChannel() { + assertEquals(1, mGroupRow.getNumUniqueChannels()); + } + + @Test + public void testGetNumUniqueChildren_multiChannel() { + List<ExpandableNotificationRow> childRows = + mGroupRow.getChildrenContainer().getNotificationChildren(); + // Give each child a unique channel id/name. + int i = 0; + for (ExpandableNotificationRow childRow : childRows) { + childRow.getEntry().channel = + new NotificationChannel("id" + i, "dinnertime" + i, IMPORTANCE_DEFAULT); + i++; + } + + assertEquals(3, mGroupRow.getNumUniqueChannels()); + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationBlockingHelperManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationBlockingHelperManagerTest.java index 64f34e009e7f..78cceeb3ff53 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationBlockingHelperManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationBlockingHelperManagerTest.java @@ -87,7 +87,7 @@ public class NotificationBlockingHelperManagerTest extends SysuiTestCase { @Test public void testDismissCurrentBlockingHelper_withDetachedBlockingHelperRow() throws Exception { - ExpandableNotificationRow row = spy(mHelper.createRow()); + ExpandableNotificationRow row = spy(createBlockableRowSpy()); row.setBlockingHelperShowing(true); when(row.isAttachedToWindow()).thenReturn(false); mBlockingHelperManager.setBlockingHelperRowForTest(row); @@ -100,7 +100,7 @@ public class NotificationBlockingHelperManagerTest extends SysuiTestCase { @Test public void testDismissCurrentBlockingHelper_withAttachedBlockingHelperRow() throws Exception { - ExpandableNotificationRow row = spy(mHelper.createRow()); + ExpandableNotificationRow row = spy(createBlockableRowSpy()); row.setBlockingHelperShowing(true); when(row.isAttachedToWindow()).thenReturn(true); mBlockingHelperManager.setBlockingHelperRowForTest(row); @@ -113,7 +113,7 @@ public class NotificationBlockingHelperManagerTest extends SysuiTestCase { @Test public void testPerhapsShowBlockingHelper_shown() throws Exception { - ExpandableNotificationRow row = mHelper.createRow(); + ExpandableNotificationRow row = createBlockableRowSpy(); row.getEntry().userSentiment = USER_SENTIMENT_NEGATIVE; mBlockingHelperManager.setNotificationShadeExpanded(1f); @@ -125,7 +125,7 @@ public class NotificationBlockingHelperManagerTest extends SysuiTestCase { @Test public void testPerhapsShowBlockingHelper_shownForLargeGroup() throws Exception { - ExpandableNotificationRow groupRow = mHelper.createGroup(10); + ExpandableNotificationRow groupRow = createBlockableGroupRowSpy(10); groupRow.getEntry().userSentiment = USER_SENTIMENT_NEGATIVE; mBlockingHelperManager.setNotificationShadeExpanded(1f); @@ -137,7 +137,7 @@ public class NotificationBlockingHelperManagerTest extends SysuiTestCase { @Test public void testPerhapsShowBlockingHelper_shownForOnlyChildNotification() throws Exception { - ExpandableNotificationRow groupRow = mHelper.createGroup(1); + ExpandableNotificationRow groupRow = createBlockableGroupRowSpy(1); // Explicitly get the children container & call getViewAtPosition on it instead of the row // as other factors such as view expansion may cause us to get the parent row back instead // of the child row. @@ -152,7 +152,7 @@ public class NotificationBlockingHelperManagerTest extends SysuiTestCase { @Test public void testPerhapsShowBlockingHelper_notShownDueToNeutralUserSentiment() throws Exception { - ExpandableNotificationRow row = mHelper.createRow(); + ExpandableNotificationRow row = createBlockableRowSpy(); row.getEntry().userSentiment = USER_SENTIMENT_NEUTRAL; mBlockingHelperManager.setNotificationShadeExpanded(1f); @@ -162,7 +162,7 @@ public class NotificationBlockingHelperManagerTest extends SysuiTestCase { @Test public void testPerhapsShowBlockingHelper_notShownDueToPositiveUserSentiment() throws Exception { - ExpandableNotificationRow row = mHelper.createRow(); + ExpandableNotificationRow row = createBlockableRowSpy(); row.getEntry().userSentiment = USER_SENTIMENT_POSITIVE; mBlockingHelperManager.setNotificationShadeExpanded(1f); @@ -171,7 +171,7 @@ public class NotificationBlockingHelperManagerTest extends SysuiTestCase { @Test public void testPerhapsShowBlockingHelper_notShownDueToShadeVisibility() throws Exception { - ExpandableNotificationRow row = mHelper.createRow(); + ExpandableNotificationRow row = createBlockableRowSpy(); row.getEntry().userSentiment = USER_SENTIMENT_NEGATIVE; // Hide the shade mBlockingHelperManager.setNotificationShadeExpanded(0f); @@ -180,9 +180,19 @@ public class NotificationBlockingHelperManagerTest extends SysuiTestCase { } @Test + public void testPerhapsShowBlockingHelper_notShownDueToNonblockability() throws Exception { + ExpandableNotificationRow row = createBlockableRowSpy(); + when(row.getIsNonblockable()).thenReturn(true); + row.getEntry().userSentiment = USER_SENTIMENT_NEGATIVE; + mBlockingHelperManager.setNotificationShadeExpanded(1f); + + assertFalse(mBlockingHelperManager.perhapsShowBlockingHelper(row, mMenuRow)); + } + + @Test public void testPerhapsShowBlockingHelper_notShownAsNotificationIsInMultipleChildGroup() throws Exception { - ExpandableNotificationRow groupRow = mHelper.createGroup(2); + ExpandableNotificationRow groupRow = createBlockableGroupRowSpy(2); // Explicitly get the children container & call getViewAtPosition on it instead of the row // as other factors such as view expansion may cause us to get the parent row back instead // of the child row. @@ -195,7 +205,7 @@ public class NotificationBlockingHelperManagerTest extends SysuiTestCase { @Test public void testBlockingHelperShowAndDismiss() throws Exception{ - ExpandableNotificationRow row = spy(mHelper.createRow()); + ExpandableNotificationRow row = spy(createBlockableRowSpy()); row.getEntry().userSentiment = USER_SENTIMENT_NEGATIVE; when(row.isAttachedToWindow()).thenReturn(true); mBlockingHelperManager.setNotificationShadeExpanded(1f); @@ -211,4 +221,18 @@ public class NotificationBlockingHelperManagerTest extends SysuiTestCase { verify(mEntryManager).updateNotifications(); } + + private ExpandableNotificationRow createBlockableRowSpy() throws Exception { + ExpandableNotificationRow row = spy(mHelper.createRow()); + when(row.getIsNonblockable()).thenReturn(false); + return row; + } + + private ExpandableNotificationRow createBlockableGroupRowSpy(int numChildren) throws Exception { + ExpandableNotificationRow row = spy(mHelper.createGroup(numChildren)); + when(row.getIsNonblockable()).thenReturn(false); + return row; + } + + } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationGutsManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationGutsManagerTest.java index 21f6750c0cdb..0ef2d051e4aa 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationGutsManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationGutsManagerTest.java @@ -268,9 +268,10 @@ public class NotificationGutsManagerTest extends SysuiTestCase { @Test public void testInitializeNotificationInfoView_showBlockingHelper() throws Exception { NotificationInfo notificationInfoView = mock(NotificationInfo.class); - ExpandableNotificationRow row = mHelper.createRow(); + ExpandableNotificationRow row = spy(mHelper.createRow()); row.setBlockingHelperShowing(true); row.getEntry().userSentiment = USER_SENTIMENT_NEGATIVE; + when(row.getIsNonblockable()).thenReturn(false); StatusBarNotification statusBarNotification = row.getStatusBarNotification(); mGutsManager.initializeNotificationInfo(row, notificationInfoView); @@ -285,7 +286,7 @@ public class NotificationGutsManagerTest extends SysuiTestCase { any(NotificationInfo.CheckSaveListener.class), any(NotificationInfo.OnSettingsClickListener.class), any(NotificationInfo.OnAppSettingsClickListener.class), - any(), + eq(false), eq(true) /* isForBlockingHelper */, eq(true) /* isUserSentimentNegative */); } @@ -293,9 +294,10 @@ public class NotificationGutsManagerTest extends SysuiTestCase { @Test public void testInitializeNotificationInfoView_dontShowBlockingHelper() throws Exception { NotificationInfo notificationInfoView = mock(NotificationInfo.class); - ExpandableNotificationRow row = mHelper.createRow(); + ExpandableNotificationRow row = spy(mHelper.createRow()); row.setBlockingHelperShowing(false); row.getEntry().userSentiment = USER_SENTIMENT_NEGATIVE; + when(row.getIsNonblockable()).thenReturn(false); StatusBarNotification statusBarNotification = row.getStatusBarNotification(); mGutsManager.initializeNotificationInfo(row, notificationInfoView); @@ -310,7 +312,7 @@ public class NotificationGutsManagerTest extends SysuiTestCase { any(NotificationInfo.CheckSaveListener.class), any(NotificationInfo.OnSettingsClickListener.class), any(NotificationInfo.OnAppSettingsClickListener.class), - any(), + eq(false), eq(false) /* isForBlockingHelper */, eq(true) /* isUserSentimentNegative */); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationInfoTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationInfoTest.java index c2cb5b9940eb..5b54e4d0522d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationInfoTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationInfoTest.java @@ -76,7 +76,6 @@ import org.mockito.junit.MockitoJUnit; import org.mockito.junit.MockitoRule; import java.util.ArrayList; -import java.util.Collections; import java.util.List; import java.util.concurrent.CountDownLatch; @@ -87,6 +86,7 @@ public class NotificationInfoTest extends SysuiTestCase { private static final String TEST_PACKAGE_NAME = "test_package"; private static final String TEST_SYSTEM_PACKAGE_NAME = PRINT_SPOOLER_PACKAGE_NAME; private static final int TEST_UID = 1; + private static final int MULTIPLE_CHANNEL_COUNT = 2; private static final String TEST_CHANNEL = "test_channel"; private static final String TEST_CHANNEL_NAME = "TEST CHANNEL NAME"; @@ -157,7 +157,7 @@ public class NotificationInfoTest extends SysuiTestCase { public void testBindNotification_SetsTextApplicationName() throws Exception { when(mMockPackageManager.getApplicationLabel(any())).thenReturn("App Name"); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, null); + TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, false); final TextView textView = mNotificationInfo.findViewById(R.id.pkgname); assertTrue(textView.getText().toString().contains("App Name")); assertEquals(VISIBLE, mNotificationInfo.findViewById(R.id.header).getVisibility()); @@ -169,7 +169,7 @@ public class NotificationInfoTest extends SysuiTestCase { when(mMockPackageManager.getApplicationIcon(any(ApplicationInfo.class))) .thenReturn(iconDrawable); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, null); + TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, false); final ImageView iconView = mNotificationInfo.findViewById(R.id.pkgicon); assertEquals(iconDrawable, iconView.getDrawable()); } @@ -177,7 +177,7 @@ public class NotificationInfoTest extends SysuiTestCase { @Test public void testBindNotification_GroupNameHiddenIfNoGroup() throws Exception { mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, null); + TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, false); final TextView groupNameView = mNotificationInfo.findViewById(R.id.group_name); assertEquals(GONE, groupNameView.getVisibility()); final TextView groupDividerView = mNotificationInfo.findViewById(R.id.pkg_group_divider); @@ -193,7 +193,7 @@ public class NotificationInfoTest extends SysuiTestCase { eq("test_group_id"), eq(TEST_PACKAGE_NAME), eq(TEST_UID))) .thenReturn(notificationChannelGroup); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, null); + TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, false); final TextView groupNameView = mNotificationInfo.findViewById(R.id.group_name); assertEquals(View.VISIBLE, groupNameView.getVisibility()); assertEquals("Test Group Name", groupNameView.getText()); @@ -204,7 +204,7 @@ public class NotificationInfoTest extends SysuiTestCase { @Test public void testBindNotification_SetsTextChannelName() throws Exception { mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, null); + TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, false); final TextView textView = mNotificationInfo.findViewById(R.id.channel_name); assertEquals(TEST_CHANNEL_NAME, textView.getText()); } @@ -212,7 +212,7 @@ public class NotificationInfoTest extends SysuiTestCase { @Test public void testBindNotification_DefaultChannelDoesNotUseChannelName() throws Exception { mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, mDefaultNotificationChannel, 1, mSbn, null, null, null, null); + TEST_PACKAGE_NAME, mDefaultNotificationChannel, 1, mSbn, null, null, null, false); final TextView textView = mNotificationInfo.findViewById(R.id.channel_name); assertEquals(GONE, textView.getVisibility()); } @@ -224,7 +224,7 @@ public class NotificationInfoTest extends SysuiTestCase { when(mMockINotificationManager.getNumNotificationChannelsForPackage( eq(TEST_PACKAGE_NAME), eq(TEST_UID), anyBoolean())).thenReturn(10); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, mDefaultNotificationChannel, 1, mSbn, null, null, null, null); + TEST_PACKAGE_NAME, mDefaultNotificationChannel, 1, mSbn, null, null, null, false); final TextView textView = mNotificationInfo.findViewById(R.id.channel_name); assertEquals(VISIBLE, textView.getVisibility()); } @@ -232,8 +232,7 @@ public class NotificationInfoTest extends SysuiTestCase { @Test public void testBindNotification_UnblockablePackageUsesChannelName() throws Exception { mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, - Collections.singleton(TEST_PACKAGE_NAME)); + TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true); final TextView textView = mNotificationInfo.findViewById(R.id.channel_name); assertEquals(VISIBLE, textView.getVisibility()); } @@ -241,7 +240,7 @@ public class NotificationInfoTest extends SysuiTestCase { @Test public void testBindNotification_BlockButton() throws Exception { mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, null); + TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, false); final View block = mNotificationInfo.findViewById(R.id.block); final View minimize = mNotificationInfo.findViewById(R.id.minimize); assertEquals(VISIBLE, block.getVisibility()); @@ -252,7 +251,7 @@ public class NotificationInfoTest extends SysuiTestCase { public void testBindNotification_MinButton() throws Exception { mSbn.getNotification().flags = Notification.FLAG_FOREGROUND_SERVICE; mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, null); + TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, false); final View block = mNotificationInfo.findViewById(R.id.block); final View minimize = mNotificationInfo.findViewById(R.id.minimize); assertEquals(GONE, block.getVisibility()); @@ -267,7 +266,7 @@ public class NotificationInfoTest extends SysuiTestCase { (View v, NotificationChannel c, int appUid) -> { assertEquals(mNotificationChannel, c); latch.countDown(); - }, null, null); + }, null, false); final View settingsButton = mNotificationInfo.findViewById(R.id.info); settingsButton.performClick(); @@ -278,7 +277,7 @@ public class NotificationInfoTest extends SysuiTestCase { @Test public void testBindNotification_SettingsButtonInvisibleWhenNoClickListener() throws Exception { mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, null); + TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, false); final View settingsButton = mNotificationInfo.findViewById(R.id.info); assertTrue(settingsButton.getVisibility() != View.VISIBLE); } @@ -286,11 +285,11 @@ public class NotificationInfoTest extends SysuiTestCase { @Test public void testBindNotification_SettingsButtonReappearsAfterSecondBind() throws Exception { mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, null); + TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, false); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, (View v, NotificationChannel c, int appUid) -> { - }, null, null); + }, null, false); final View settingsButton = mNotificationInfo.findViewById(R.id.info); assertEquals(View.VISIBLE, settingsButton.getVisibility()); } @@ -299,11 +298,11 @@ public class NotificationInfoTest extends SysuiTestCase { public void testOnClickListenerPassesNullChannelForBundle() throws Exception { final CountDownLatch latch = new CountDownLatch(1); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, mNotificationChannel, 2, mSbn, null, + TEST_PACKAGE_NAME, mNotificationChannel, MULTIPLE_CHANNEL_COUNT, mSbn, null, (View v, NotificationChannel c, int appUid) -> { assertEquals(null, c); latch.countDown(); - }, null, null); + }, null, true); mNotificationInfo.findViewById(R.id.info).performClick(); // Verify that listener was triggered. @@ -315,7 +314,8 @@ public class NotificationInfoTest extends SysuiTestCase { public void testBindNotification_ChannelNameInvisibleWhenBundleFromDifferentChannels() throws Exception { mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, mNotificationChannel, 2, mSbn, null, null, null, null); + TEST_PACKAGE_NAME, mNotificationChannel, MULTIPLE_CHANNEL_COUNT, mSbn, null, null, + null, true); final TextView channelNameView = mNotificationInfo.findViewById(R.id.channel_name); assertEquals(GONE, channelNameView.getVisibility()); @@ -325,7 +325,8 @@ public class NotificationInfoTest extends SysuiTestCase { @UiThreadTest public void testStopInvisibleIfBundleFromDifferentChannels() throws Exception { mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, mNotificationChannel, 2, mSbn, null, null, null, null); + TEST_PACKAGE_NAME, mNotificationChannel, MULTIPLE_CHANNEL_COUNT, mSbn, null, null, + null, true); final TextView blockView = mNotificationInfo.findViewById(R.id.block); assertEquals(GONE, blockView.getVisibility()); } @@ -333,8 +334,8 @@ public class NotificationInfoTest extends SysuiTestCase { @Test public void testbindNotification_BlockingHelper() throws Exception { mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, - null, null, false, true); + TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, false, false, + true); final TextView view = mNotificationInfo.findViewById(R.id.block_prompt); assertEquals(View.VISIBLE, view.getVisibility()); assertEquals(mContext.getString(R.string.inline_blocking_helper), view.getText()); @@ -343,8 +344,7 @@ public class NotificationInfoTest extends SysuiTestCase { @Test public void testbindNotification_UnblockableTextVisibleWhenAppUnblockable() throws Exception { mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, - null, Collections.singleton(TEST_PACKAGE_NAME)); + TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true); final TextView view = mNotificationInfo.findViewById(R.id.block_prompt); assertEquals(View.VISIBLE, view.getVisibility()); assertEquals(mContext.getString(R.string.notification_unblockable_desc), @@ -354,7 +354,7 @@ public class NotificationInfoTest extends SysuiTestCase { @Test public void testBindNotification_DoesNotUpdateNotificationChannel() throws Exception { mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, null); + TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, false); verify(mMockINotificationManager, never()).updateNotificationChannelForPackage( anyString(), eq(TEST_UID), any()); } @@ -363,7 +363,7 @@ public class NotificationInfoTest extends SysuiTestCase { public void testDoesNotUpdateNotificationChannelAfterImportanceChanged() throws Exception { mNotificationChannel.setImportance(IMPORTANCE_LOW); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, null); + TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, false); mNotificationInfo.findViewById(R.id.block).performClick(); verify(mMockINotificationManager, never()).updateNotificationChannelForPackage( @@ -375,7 +375,7 @@ public class NotificationInfoTest extends SysuiTestCase { throws Exception { mNotificationChannel.setImportance(IMPORTANCE_LOW); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, null); + TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, false); mNotificationInfo.findViewById(R.id.minimize).performClick(); verify(mMockINotificationManager, never()).updateNotificationChannelForPackage( @@ -387,7 +387,7 @@ public class NotificationInfoTest extends SysuiTestCase { throws Exception { int originalImportance = mNotificationChannel.getImportance(); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, null); + TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, false); mNotificationInfo.handleCloseControls(true, false); verify(mMockINotificationManager, times(1)).updateNotificationChannelForPackage( @@ -400,7 +400,7 @@ public class NotificationInfoTest extends SysuiTestCase { throws Exception { mNotificationChannel.setImportance(IMPORTANCE_UNSPECIFIED); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, null); + TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, false); mNotificationInfo.handleCloseControls(true, false); verify(mMockINotificationManager, times(1)).updateNotificationChannelForPackage( @@ -420,7 +420,7 @@ public class NotificationInfoTest extends SysuiTestCase { null /* checkSaveListener */, null /* onSettingsClick */, null /* onAppSettingsClick */, - null /* nonBlockablePkgs */, + false /* isNonblockable */, true /* isForBlockingHelper */, false /* isUserSentimentNegative */); @@ -433,8 +433,7 @@ public class NotificationInfoTest extends SysuiTestCase { public void testNonBlockableAppDoesNotBecomeBlocked() throws Exception { mNotificationChannel.setImportance(IMPORTANCE_LOW); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, - null, Collections.singleton(TEST_PACKAGE_NAME)); + TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true); mNotificationInfo.findViewById(R.id.block).performClick(); waitForUndoButton(); verify(mMockINotificationManager, never()).updateNotificationChannelForPackage( @@ -445,7 +444,7 @@ public class NotificationInfoTest extends SysuiTestCase { public void testBlockChangedCallsUpdateNotificationChannel() throws Exception { mNotificationChannel.setImportance(IMPORTANCE_LOW); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, null); + TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, false); mNotificationInfo.findViewById(R.id.block).performClick(); waitForUndoButton(); @@ -464,8 +463,7 @@ public class NotificationInfoTest extends SysuiTestCase { public void testNonBlockableAppDoesNotBecomeMin() throws Exception { mNotificationChannel.setImportance(IMPORTANCE_LOW); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, - null, Collections.singleton(TEST_PACKAGE_NAME)); + TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true); mNotificationInfo.findViewById(R.id.minimize).performClick(); waitForUndoButton(); verify(mMockINotificationManager, never()).updateNotificationChannelForPackage( @@ -477,7 +475,7 @@ public class NotificationInfoTest extends SysuiTestCase { mNotificationChannel.setImportance(IMPORTANCE_LOW); mSbn.getNotification().flags = Notification.FLAG_FOREGROUND_SERVICE; mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, null); + TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, false); mNotificationInfo.findViewById(R.id.minimize).performClick(); waitForUndoButton(); @@ -496,7 +494,7 @@ public class NotificationInfoTest extends SysuiTestCase { public void testKeepUpdatesNotificationChannel() throws Exception { mNotificationChannel.setImportance(IMPORTANCE_LOW); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, null); + TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, false); mNotificationInfo.handleCloseControls(true, false); @@ -512,7 +510,7 @@ public class NotificationInfoTest extends SysuiTestCase { public void testBlockUndoDoesNotBlockNotificationChannel() throws Exception { mNotificationChannel.setImportance(IMPORTANCE_LOW); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, null); + TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, false); mNotificationInfo.findViewById(R.id.block).performClick(); waitForUndoButton(); @@ -532,7 +530,7 @@ public class NotificationInfoTest extends SysuiTestCase { public void testMinUndoDoesNotMinNotificationChannel() throws Exception { mNotificationChannel.setImportance(IMPORTANCE_LOW); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, null); + TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, false); mNotificationInfo.findViewById(R.id.minimize).performClick(); waitForUndoButton(); @@ -552,8 +550,7 @@ public class NotificationInfoTest extends SysuiTestCase { public void testCloseControlsDoesNotUpdateiMinIfSaveIsFalse() throws Exception { mNotificationChannel.setImportance(IMPORTANCE_LOW); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, - Collections.singleton(TEST_PACKAGE_NAME)); + TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true); mNotificationInfo.findViewById(R.id.minimize).performClick(); waitForUndoButton(); @@ -566,8 +563,7 @@ public class NotificationInfoTest extends SysuiTestCase { public void testCloseControlsDoesNotUpdateIfSaveIsFalse() throws Exception { mNotificationChannel.setImportance(IMPORTANCE_LOW); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, - Collections.singleton(TEST_PACKAGE_NAME)); + TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true); mNotificationInfo.findViewById(R.id.block).performClick(); waitForUndoButton(); @@ -582,7 +578,7 @@ public class NotificationInfoTest extends SysuiTestCase { mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, (Runnable saveImportance, StatusBarNotification sbn) -> { - }, null, null, Collections.singleton(TEST_PACKAGE_NAME)); + }, null, null, true); mNotificationInfo.findViewById(R.id.block).performClick(); waitForUndoButton(); @@ -598,7 +594,7 @@ public class NotificationInfoTest extends SysuiTestCase { TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, (Runnable saveImportance, StatusBarNotification sbn) -> { saveImportance.run(); - }, null, null, null); + }, null, null, false); mNotificationInfo.findViewById(R.id.block).performClick(); waitForUndoButton(); @@ -628,7 +624,7 @@ public class NotificationInfoTest extends SysuiTestCase { TEST_PACKAGE_NAME, mNotificationChannel, 1, sbn, null, null, (View v, Intent intent) -> { latch.countDown(); - }, null); + }, false); final TextView settingsLink = mNotificationInfo.findViewById(R.id.app_settings); assertEquals(View.VISIBLE, settingsLink.getVisibility()); settingsLink.performClick(); @@ -653,10 +649,10 @@ public class NotificationInfoTest extends SysuiTestCase { 0, null, 0, 0, n, UserHandle.CURRENT, null, 0); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, mNotificationChannel, 2, sbn, null, null, + TEST_PACKAGE_NAME, mNotificationChannel, MULTIPLE_CHANNEL_COUNT, sbn, null, null, (View v, Intent intent) -> { latch.countDown(); - }, null); + }, false); final TextView settingsLink = mNotificationInfo.findViewById(R.id.app_settings); assertEquals(View.VISIBLE, settingsLink.getVisibility()); settingsLink.performClick(); @@ -674,7 +670,8 @@ public class NotificationInfoTest extends SysuiTestCase { 0, null, 0, 0, n, UserHandle.CURRENT, null, 0); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, mNotificationChannel, 2, sbn, null, null, null, null); + TEST_PACKAGE_NAME, mNotificationChannel, MULTIPLE_CHANNEL_COUNT, sbn, null, null, + null, false); final TextView settingsLink = mNotificationInfo.findViewById(R.id.app_settings); assertEquals(GONE, settingsLink.getVisibility()); } @@ -694,7 +691,30 @@ public class NotificationInfoTest extends SysuiTestCase { 0, null, 0, 0, n, UserHandle.CURRENT, null, 0); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, mNotificationChannel, 1, sbn, null, null, null, null); + TEST_PACKAGE_NAME, mNotificationChannel, 1, sbn, null, null, null, false); + final TextView settingsLink = mNotificationInfo.findViewById(R.id.app_settings); + assertEquals(GONE, settingsLink.getVisibility()); + } + + @Test + public void testBindHeader_noSettingsLinkWhenIsForBlockingHelper() throws Exception { + final String settingsText = "work chats"; + final ResolveInfo ri = new ResolveInfo(); + ri.activityInfo = new ActivityInfo(); + ri.activityInfo.packageName = TEST_PACKAGE_NAME; + ri.activityInfo.name = "something"; + List<ResolveInfo> ris = new ArrayList<>(); + ris.add(ri); + when(mMockPackageManager.queryIntentActivities(any(), anyInt())).thenReturn(ris); + mNotificationChannel.setImportance(IMPORTANCE_LOW); + Notification n = new Notification.Builder(mContext, mNotificationChannel.getId()) + .setSettingsText(settingsText).build(); + StatusBarNotification sbn = new StatusBarNotification(TEST_PACKAGE_NAME, TEST_PACKAGE_NAME, + 0, null, 0, 0, n, UserHandle.CURRENT, null, 0); + + mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, + TEST_PACKAGE_NAME, mNotificationChannel, 1, sbn, null, null, null, null, true, + true); final TextView settingsLink = mNotificationInfo.findViewById(R.id.app_settings); assertEquals(GONE, settingsLink.getVisibility()); } @@ -710,8 +730,7 @@ public class NotificationInfoTest extends SysuiTestCase { mSbn.getNotification().flags = Notification.FLAG_FOREGROUND_SERVICE; mNotificationChannel.setImportance(IMPORTANCE_LOW); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, - Collections.singleton(TEST_PACKAGE_NAME)); + TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true); mNotificationInfo.findViewById(R.id.minimize).performClick(); waitForUndoButton(); @@ -723,8 +742,7 @@ public class NotificationInfoTest extends SysuiTestCase { public void testUndoText_block() throws Exception { mNotificationChannel.setImportance(IMPORTANCE_LOW); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, - Collections.singleton(TEST_PACKAGE_NAME)); + TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true); mNotificationInfo.findViewById(R.id.block).performClick(); waitForUndoButton(); @@ -736,8 +754,7 @@ public class NotificationInfoTest extends SysuiTestCase { public void testNoHeaderOnConfirmation() throws Exception { mNotificationChannel.setImportance(IMPORTANCE_LOW); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, - Collections.singleton(TEST_PACKAGE_NAME)); + TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true); mNotificationInfo.findViewById(R.id.block).performClick(); waitForUndoButton(); @@ -748,8 +765,7 @@ public class NotificationInfoTest extends SysuiTestCase { public void testHeaderOnUndo() throws Exception { mNotificationChannel.setImportance(IMPORTANCE_LOW); mNotificationInfo.bindNotification(mMockPackageManager, mMockINotificationManager, - TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, - Collections.singleton(TEST_PACKAGE_NAME)); + TEST_PACKAGE_NAME, mNotificationChannel, 1, mSbn, null, null, null, true); mNotificationInfo.findViewById(R.id.block).performClick(); waitForUndoButton(); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarIconListTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarIconListTest.java index 2792d8c45865..07ac11b82e8c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarIconListTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarIconListTest.java @@ -1,17 +1,23 @@ package com.android.systemui.statusbar; +import static com.android.systemui.statusbar.phone.StatusBarIconController.TAG_PRIMARY; import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertNull; +import static junit.framework.Assert.assertTrue; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; import android.support.test.runner.AndroidJUnit4; import android.test.suitebuilder.annotation.SmallTest; -import com.android.internal.statusbar.StatusBarIcon; import com.android.systemui.SysuiTestCase; +import com.android.systemui.statusbar.phone.StatusBarIconHolder; import com.android.systemui.statusbar.phone.StatusBarIconList; +import com.android.systemui.statusbar.phone.StatusBarIconList.Slot; +import java.util.ArrayList; +import java.util.List; import org.junit.Test; import org.junit.runner.RunWith; @@ -41,45 +47,125 @@ public class StatusBarIconListTest extends SysuiTestCase { @Test public void testAddSlotSlidesIcons() { StatusBarIconList statusBarIconList = new StatusBarIconList(STATUS_BAR_SLOTS); - StatusBarIcon sbIcon = mock(StatusBarIcon.class); - statusBarIconList.setIcon(0, sbIcon); + StatusBarIconHolder sbHolder = mock(StatusBarIconHolder.class); + statusBarIconList.setIcon(0, sbHolder); statusBarIconList.getSlotIndex("zzz"); // new content added in front - assertNull(statusBarIconList.getIcon(0)); - assertEquals(sbIcon, statusBarIconList.getIcon(1)); + assertNull(statusBarIconList.getIcon(0, TAG_PRIMARY)); + assertEquals(sbHolder, statusBarIconList.getIcon(1, TAG_PRIMARY)); } @Test public void testGetAndSetIcon() { StatusBarIconList statusBarIconList = new StatusBarIconList(STATUS_BAR_SLOTS); - StatusBarIcon sbIconA = mock(StatusBarIcon.class); - StatusBarIcon sbIconB = mock(StatusBarIcon.class); - statusBarIconList.setIcon(0, sbIconA); - statusBarIconList.setIcon(1, sbIconB); - assertEquals(sbIconA, statusBarIconList.getIcon(0)); - assertEquals(sbIconB, statusBarIconList.getIcon(1)); - assertNull(statusBarIconList.getIcon(2)); // icon not set + StatusBarIconHolder sbHolderA = mock(StatusBarIconHolder.class); + StatusBarIconHolder sbHolderB = mock(StatusBarIconHolder.class); + statusBarIconList.setIcon(0, sbHolderA); + statusBarIconList.setIcon(1, sbHolderB); + assertEquals(sbHolderA, statusBarIconList.getIcon(0, TAG_PRIMARY)); + assertEquals(sbHolderB, statusBarIconList.getIcon(1, TAG_PRIMARY)); + assertNull(statusBarIconList.getIcon(2, TAG_PRIMARY)); // icon not set } @Test public void testRemoveIcon() { StatusBarIconList statusBarIconList = new StatusBarIconList(STATUS_BAR_SLOTS); - StatusBarIcon sbIconA = mock(StatusBarIcon.class); - StatusBarIcon sbIconB = mock(StatusBarIcon.class); - statusBarIconList.setIcon(0, sbIconA); - statusBarIconList.setIcon(1, sbIconB); - statusBarIconList.removeIcon(0); - assertNull(statusBarIconList.getIcon(0)); // icon not set + StatusBarIconHolder sbHolderA = mock(StatusBarIconHolder.class); + StatusBarIconHolder sbHolderB = mock(StatusBarIconHolder.class); + statusBarIconList.setIcon(0, sbHolderA); + statusBarIconList.setIcon(1, sbHolderB); + statusBarIconList.removeIcon(0, TAG_PRIMARY); + assertNull(statusBarIconList.getIcon(0, TAG_PRIMARY)); // icon not set } @Test - public void testGetViewIndex() { + public void testGetViewIndex_NoMultiples() { StatusBarIconList statusBarIconList = new StatusBarIconList(STATUS_BAR_SLOTS); - StatusBarIcon sbIcon = mock(StatusBarIcon.class); - statusBarIconList.setIcon(2, sbIcon); - assertEquals(0, statusBarIconList.getViewIndex(2)); // Icon for item 2 is 0th child view. - statusBarIconList.setIcon(0, sbIcon); - assertEquals(0, statusBarIconList.getViewIndex(0)); // Icon for item 0 is 0th child view, - assertEquals(1, statusBarIconList.getViewIndex(2)); // and item 2 is now 1st child view. + StatusBarIconHolder sbHolder = mock(StatusBarIconHolder.class); + statusBarIconList.setIcon(2, sbHolder); + // Icon for item 2 is 0th child view. + assertEquals(0, statusBarIconList.getViewIndex(2, TAG_PRIMARY)); + statusBarIconList.setIcon(0, sbHolder); + // Icon for item 0 is 0th child view, + assertEquals(0, statusBarIconList.getViewIndex(0, TAG_PRIMARY)); + // and item 2 is now 1st child view. + assertEquals(1, statusBarIconList.getViewIndex(2, TAG_PRIMARY)); } + @Test + public void testGetViewIndex_MultipleIconsPerSlot() { + StatusBarIconList statusBarIconList = new StatusBarIconList(STATUS_BAR_SLOTS); + StatusBarIconHolder sbHolder = mock(StatusBarIconHolder.class); + + statusBarIconList.setIcon(2, sbHolder); // item 2, one icon 0th child + + // All of these can be added to the same slot + // no tag bc it defaults to 0 + StatusBarIconHolder sbHolder2 = mock(StatusBarIconHolder.class); + StatusBarIconHolder sbHolder3 = mock(StatusBarIconHolder.class); + int sb3Tag = 1; + when(sbHolder3.getTag()).thenReturn(sb3Tag); + StatusBarIconHolder sbHolder4 = mock(StatusBarIconHolder.class); + int sb4Tag = 2; + when(sbHolder4.getTag()).thenReturn(sb4Tag); + + // Put a holder at slot 1, verify that it is first + statusBarIconList.setIcon(1, sbHolder2); + assertEquals(0, statusBarIconList.getViewIndex(1, TAG_PRIMARY)); + + // Put another holder at slot 1, verify it's index 0 and the rest come after + statusBarIconList.setIcon(1, sbHolder3); + assertEquals(0, statusBarIconList.getViewIndex(1, sb3Tag)); + assertEquals(1, statusBarIconList.getViewIndex(1, TAG_PRIMARY)); + // First icon should be at the end + assertEquals(2, statusBarIconList.getViewIndex(2, TAG_PRIMARY)); + + // Put another one in there just for good measure + statusBarIconList.setIcon(1, sbHolder4); + assertEquals(0, statusBarIconList.getViewIndex(1, sb4Tag)); + assertEquals(1, statusBarIconList.getViewIndex(1, sb3Tag)); + assertEquals(2, statusBarIconList.getViewIndex(1, TAG_PRIMARY)); + assertEquals(3, statusBarIconList.getViewIndex(2, TAG_PRIMARY)); + } + + /** + * StatusBarIconList.Slot tests + */ + + @Test + public void testSlot_OrderIsPreserved() { + Slot testSlot = new Slot("test_name", null); + + // no tag bc it defaults to 0 + StatusBarIconHolder sbHolder1 = mock(StatusBarIconHolder.class); + StatusBarIconHolder sbHolder2 = mock(StatusBarIconHolder.class); + int sb2Tag = 1; + when(sbHolder2.getTag()).thenReturn(sb2Tag); + StatusBarIconHolder sbHolder3 = mock(StatusBarIconHolder.class); + int sb3Tag = 2; + when(sbHolder3.getTag()).thenReturn(sb3Tag); + + ArrayList<StatusBarIconHolder> expected = new ArrayList<>(); + expected.add(sbHolder1); + expected.add(sbHolder2); + expected.add(sbHolder3); + + + // Add 3 icons in the same slot, and verify that the list we get is equal to what we gave + for (StatusBarIconHolder holder : expected) { + testSlot.addHolder(holder); + } + assertTrue(listsEqual(expected, testSlot.getHolderList())); + } + + private boolean listsEqual(List<StatusBarIconHolder> list1, List<StatusBarIconHolder> list2) { + if (list1.size() != list2.size()) return false; + + for (int i = 0; i < list1.size(); i++) { + if (!list1.get(i).equals(list2.get(i))) { + return false; + } + } + + return true; + } }
\ No newline at end of file diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarIconControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarIconControllerTest.java index 5db77925b25e..72b0156d25f9 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarIconControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarIconControllerTest.java @@ -14,10 +14,17 @@ package com.android.systemui.statusbar.phone; +import static junit.framework.Assert.assertEquals; +import static junit.framework.Assert.assertTrue; + +import static com.android.systemui.statusbar.phone.StatusBarIconHolder.TYPE_ICON; +import static com.android.systemui.statusbar.phone.StatusBarIconHolder.TYPE_MOBILE; +import static com.android.systemui.statusbar.phone.StatusBarIconHolder.TYPE_WIFI; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; +import android.graphics.Rect; import android.support.test.filters.SmallTest; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper.RunWithLooper; @@ -25,9 +32,14 @@ import android.view.ViewGroup; import android.widget.LinearLayout; import com.android.internal.statusbar.StatusBarIcon; +import com.android.systemui.statusbar.StatusIconDisplayable; import com.android.systemui.statusbar.StatusBarIconView; +import com.android.systemui.statusbar.StatusBarMobileView; +import com.android.systemui.statusbar.StatusBarWifiView; import com.android.systemui.statusbar.phone.StatusBarIconController.DarkIconManager; import com.android.systemui.statusbar.phone.StatusBarIconController.IconManager; +import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.MobileIconState; +import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.WifiIconState; import com.android.systemui.statusbar.policy.DarkIconDispatcher; import com.android.systemui.utils.leaks.LeakCheckedTest; @@ -50,50 +62,119 @@ public class StatusBarIconControllerTest extends LeakCheckedTest { public void testSetCalledOnAdd_IconManager() { LinearLayout layout = new LinearLayout(mContext); TestIconManager manager = new TestIconManager(layout); - StatusBarIcon icon = mock(StatusBarIcon.class); - - manager.onIconAdded(0, "test_slot", false, icon); - verify(manager.mMock).set(eq(icon)); + testCallOnAdd_forManager(manager); } @Test public void testSetCalledOnAdd_DarkIconManager() { LinearLayout layout = new LinearLayout(mContext); TestDarkIconManager manager = new TestDarkIconManager(layout); - StatusBarIcon icon = mock(StatusBarIcon.class); + testCallOnAdd_forManager(manager); + } + + + private <T extends IconManager & TestableIconManager> void testCallOnAdd_forManager(T manager) { + StatusBarIconHolder holder = holderForType(TYPE_ICON); + manager.onIconAdded(0, "test_slot", false, holder); + assertTrue("Expected StatusBarIconView", + (manager.getViewAt(0) instanceof StatusBarIconView)); - manager.onIconAdded(0, "test_slot", false, icon); - verify(manager.mMock).set(eq(icon)); + holder = holderForType(TYPE_WIFI); + manager.onIconAdded(1, "test_wifi", false, holder); + assertTrue(manager.getViewAt(1) instanceof StatusBarWifiView); + + holder = holderForType(TYPE_MOBILE); + manager.onIconAdded(2, "test_mobile", false, holder); + assertTrue(manager.getViewAt(2) instanceof StatusBarMobileView); } - private static class TestDarkIconManager extends DarkIconManager { + private StatusBarIconHolder holderForType(int type) { + switch (type) { + case TYPE_MOBILE: + return StatusBarIconHolder.fromMobileIconState(mock(MobileIconState.class)); + + case TYPE_WIFI: + return StatusBarIconHolder.fromWifiIconState(mock(WifiIconState.class)); - private final StatusBarIconView mMock; + case TYPE_ICON: + default: + return StatusBarIconHolder.fromIcon(mock(StatusBarIcon.class)); + } + } + + private static class TestDarkIconManager extends DarkIconManager + implements TestableIconManager { public TestDarkIconManager(LinearLayout group) { super(group); - mMock = mock(StatusBarIconView.class); } @Override - protected StatusBarIconView onCreateStatusBarIconView(String slot, boolean blocked) { - return mMock; + public StatusIconDisplayable getViewAt(int index) { + return (StatusIconDisplayable) mGroup.getChildAt(index); } - } - private static class TestIconManager extends IconManager { + @Override + protected StatusBarIconView addIcon(int index, String slot, boolean blocked, + StatusBarIcon icon) { + StatusBarIconView mock = mock(StatusBarIconView.class); + mGroup.addView(mock, index); + + return mock; + } - private final StatusBarIconView mMock; + @Override + protected StatusBarWifiView addSignalIcon(int index, String slot, WifiIconState state) { + StatusBarWifiView mock = mock(StatusBarWifiView.class); + mGroup.addView(mock, index); + return mock; + } + @Override + protected StatusBarMobileView addMobileIcon(int index, String slot, MobileIconState state) { + StatusBarMobileView mock = mock(StatusBarMobileView.class); + mGroup.addView(mock, index); + + return mock; + } + } + + private static class TestIconManager extends IconManager implements TestableIconManager { public TestIconManager(ViewGroup group) { super(group); - mMock = mock(StatusBarIconView.class); } @Override - protected StatusBarIconView onCreateStatusBarIconView(String slot, boolean blocked) { - return mMock; + public StatusIconDisplayable getViewAt(int index) { + return (StatusIconDisplayable) mGroup.getChildAt(index); + } + + @Override + protected StatusBarIconView addIcon(int index, String slot, boolean blocked, + StatusBarIcon icon) { + StatusBarIconView mock = mock(StatusBarIconView.class); + mGroup.addView(mock, index); + + return mock; + } + + @Override + protected StatusBarWifiView addSignalIcon(int index, String slot, WifiIconState state) { + StatusBarWifiView mock = mock(StatusBarWifiView.class); + mGroup.addView(mock, index); + return mock; + } + + @Override + protected StatusBarMobileView addMobileIcon(int index, String slot, MobileIconState state) { + StatusBarMobileView mock = mock(StatusBarMobileView.class); + mGroup.addView(mock, index); + + return mock; } } + private interface TestableIconManager { + StatusIconDisplayable getViewAt(int index); + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeStatusBarIconController.java b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeStatusBarIconController.java index 6b501af95097..8e34685cceec 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeStatusBarIconController.java +++ b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeStatusBarIconController.java @@ -19,6 +19,9 @@ import android.testing.LeakCheck; import com.android.internal.statusbar.StatusBarIcon; import com.android.systemui.statusbar.phone.StatusBarIconController; import com.android.systemui.statusbar.phone.StatusBarIconController.IconManager; +import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.MobileIconState; +import com.android.systemui.statusbar.phone.StatusBarSignalPolicy.WifiIconState; +import java.util.List; public class FakeStatusBarIconController extends BaseLeakChecker<IconManager> implements StatusBarIconController { @@ -53,12 +56,23 @@ public class FakeStatusBarIconController extends BaseLeakChecker<IconManager> } @Override - public void setIconVisibility(String slotTty, boolean b) { + public void setSignalIcon(String slot, WifiIconState state) { + } + + @Override + public void setMobileIcons(String slot, List<MobileIconState> states) { + } + @Override + public void setIconVisibility(String slotTty, boolean b) { } @Override - public void removeIcon(String slot) { + public void removeIcon(String slot, int tag) { + } + @Override + public void removeAllIconsForSlot(String slot) { } + } diff --git a/proto/src/wifi.proto b/proto/src/wifi.proto index 160525294c0f..934ad882a635 100644 --- a/proto/src/wifi.proto +++ b/proto/src/wifi.proto @@ -413,6 +413,15 @@ message WifiLog { // Indicates the number of times we got an interface down in softap mode. optional int32 num_soft_ap_interface_down = 103; + + // Indicates the number of scan requests from external apps. + optional int32 num_external_app_oneshot_scan_requests = 104; + + // Indicates the number of times a scan request from an external foreground app was throttled. + optional int32 num_external_foreground_app_oneshot_scan_requests_throttled = 105; + + // Indicates the number of times a scan request from an external background app was throttled. + optional int32 num_external_background_app_oneshot_scan_requests_throttled = 106; } // Information that gets logged for every WiFi connection. diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 08e5d44c8832..e7353876a775 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -14830,7 +14830,7 @@ public class ActivityManagerService extends IActivityManager.Stub .setPackage("android") .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); broadcastIntent(null, intent, null, null, 0, null, null, null, - OP_NONE, null, true, false, UserHandle.USER_ALL); + OP_NONE, null, false, false, UserHandle.USER_ALL); } finally { Binder.restoreCallingIdentity(ident); } diff --git a/services/core/java/com/android/server/connectivity/Tethering.java b/services/core/java/com/android/server/connectivity/Tethering.java index eee830f3299b..1bc74238436f 100644 --- a/services/core/java/com/android/server/connectivity/Tethering.java +++ b/services/core/java/com/android/server/connectivity/Tethering.java @@ -56,6 +56,7 @@ import android.net.NetworkInfo; import android.net.NetworkState; import android.net.NetworkUtils; import android.net.RouteInfo; +import android.net.util.InterfaceSet; import android.net.util.PrefixUtils; import android.net.util.SharedLog; import android.net.util.VersionedBroadcastListener; @@ -98,6 +99,7 @@ import com.android.server.connectivity.tethering.SimChangeListener; import com.android.server.connectivity.tethering.TetherInterfaceStateMachine; import com.android.server.connectivity.tethering.TetheringConfiguration; import com.android.server.connectivity.tethering.TetheringDependencies; +import com.android.server.connectivity.tethering.TetheringInterfaceUtils; import com.android.server.connectivity.tethering.UpstreamNetworkMonitor; import com.android.server.net.BaseNetworkObserver; @@ -181,9 +183,10 @@ public class Tethering extends BaseNetworkObserver { private final VersionedBroadcastListener mCarrierConfigChange; // TODO: Delete SimChangeListener; it's obsolete. private final SimChangeListener mSimChange; + private final TetheringDependencies mDeps; private volatile TetheringConfiguration mConfig; - private String mCurrentUpstreamIface; + private InterfaceSet mCurrentUpstreamIfaceSet; private Notification.Builder mTetheredNotificationBuilder; private int mLastNotificationId; @@ -202,12 +205,13 @@ public class Tethering extends BaseNetworkObserver { mPolicyManager = policyManager; mLooper = looper; mSystemProperties = systemProperties; + mDeps = deps; mPublicSync = new Object(); mTetherStates = new ArrayMap<>(); - mTetherMasterSM = new TetherMasterSM("TetherMaster", mLooper); + mTetherMasterSM = new TetherMasterSM("TetherMaster", mLooper, deps); mTetherMasterSM.start(); final Handler smHandler = mTetherMasterSM.getHandler(); @@ -215,8 +219,8 @@ public class Tethering extends BaseNetworkObserver { deps.getOffloadHardwareInterface(smHandler, mLog), mContext.getContentResolver(), mNMService, mLog); - mUpstreamNetworkMonitor = new UpstreamNetworkMonitor( - mContext, mTetherMasterSM, mLog, TetherMasterSM.EVENT_UPSTREAM_CALLBACK); + mUpstreamNetworkMonitor = deps.getUpstreamNetworkMonitor(mContext, mTetherMasterSM, mLog, + TetherMasterSM.EVENT_UPSTREAM_CALLBACK); mForwardedDownstreams = new HashSet<>(); IntentFilter filter = new IntentFilter(); @@ -1160,12 +1164,11 @@ public class Tethering extends BaseNetworkObserver { } // Needed because the canonical source of upstream truth is just the - // upstream interface name, |mCurrentUpstreamIface|. This is ripe for - // future simplification, once the upstream Network is canonical. + // upstream interface set, |mCurrentUpstreamIfaceSet|. private boolean pertainsToCurrentUpstream(NetworkState ns) { - if (ns != null && ns.linkProperties != null && mCurrentUpstreamIface != null) { + if (ns != null && ns.linkProperties != null && mCurrentUpstreamIfaceSet != null) { for (String ifname : ns.linkProperties.getAllInterfaceNames()) { - if (mCurrentUpstreamIface.equals(ifname)) { + if (mCurrentUpstreamIfaceSet.ifnames.contains(ifname)) { return true; } } @@ -1241,7 +1244,7 @@ public class Tethering extends BaseNetworkObserver { private static final int UPSTREAM_SETTLE_TIME_MS = 10000; - TetherMasterSM(String name, Looper looper) { + TetherMasterSM(String name, Looper looper, TetheringDependencies deps) { super(name, looper); mInitialState = new InitialState(); @@ -1261,7 +1264,7 @@ public class Tethering extends BaseNetworkObserver { addState(mSetDnsForwardersErrorState); mNotifyList = new ArrayList<>(); - mIPv6TetheringCoordinator = new IPv6TetheringCoordinator(mNotifyList, mLog); + mIPv6TetheringCoordinator = deps.getIPv6TetheringCoordinator(mNotifyList, mLog); mOffload = new OffloadWrapper(); setInitialState(mInitialState); @@ -1360,31 +1363,27 @@ public class Tethering extends BaseNetworkObserver { } protected void setUpstreamNetwork(NetworkState ns) { - String iface = null; + InterfaceSet ifaces = null; if (ns != null) { // Find the interface with the default IPv4 route. It may be the // interface described by linkProperties, or one of the interfaces // stacked on top of it. mLog.i("Looking for default routes on: " + ns.linkProperties); - final String iface4 = getIPv4DefaultRouteInterface(ns); - final String iface6 = getIPv6DefaultRouteInterface(ns); - mLog.i("IPv4/IPv6 upstream interface(s): " + iface4 + "/" + iface6); - - iface = (iface4 != null) ? iface4 : null /* TODO: iface6 */; + ifaces = TetheringInterfaceUtils.getTetheringInterfaces(ns); + mLog.i("Found upstream interface(s): " + ifaces); } - if (iface != null) { + if (ifaces != null) { setDnsForwarders(ns.network, ns.linkProperties); } - notifyDownstreamsOfNewUpstreamIface(iface); + notifyDownstreamsOfNewUpstreamIface(ifaces); if (ns != null && pertainsToCurrentUpstream(ns)) { // If we already have NetworkState for this network examine // it immediately, because there likely will be no second // EVENT_ON_AVAILABLE (it was already received). handleNewUpstreamNetworkState(ns); - } else if (mCurrentUpstreamIface == null) { - // There are no available upstream networks, or none that - // have an IPv4 default route (current metric for success). + } else if (mCurrentUpstreamIfaceSet == null) { + // There are no available upstream networks. handleNewUpstreamNetworkState(null); } } @@ -1411,12 +1410,10 @@ public class Tethering extends BaseNetworkObserver { } } - protected void notifyDownstreamsOfNewUpstreamIface(String ifaceName) { - mLog.log("Notifying downstreams of upstream=" + ifaceName); - mCurrentUpstreamIface = ifaceName; + protected void notifyDownstreamsOfNewUpstreamIface(InterfaceSet ifaces) { + mCurrentUpstreamIfaceSet = ifaces; for (TetherInterfaceStateMachine sm : mNotifyList) { - sm.sendMessage(TetherInterfaceStateMachine.CMD_TETHER_CONNECTION_CHANGED, - ifaceName); + sm.sendMessage(TetherInterfaceStateMachine.CMD_TETHER_CONNECTION_CHANGED, ifaces); } } @@ -1488,7 +1485,7 @@ public class Tethering extends BaseNetworkObserver { // For example, after CONNECTIVITY_ACTION listening is removed, here // is where we could observe a Wi-Fi network becoming available and // passing validation. - if (mCurrentUpstreamIface == null) { + if (mCurrentUpstreamIfaceSet == null) { // If we have no upstream interface, try to run through upstream // selection again. If, for example, IPv4 connectivity has shown up // after IPv6 (e.g., 464xlat became available) we want the chance to @@ -1512,8 +1509,7 @@ public class Tethering extends BaseNetworkObserver { handleNewUpstreamNetworkState(ns); break; case UpstreamNetworkMonitor.EVENT_ON_LINKPROPERTIES: - setDnsForwarders(ns.network, ns.linkProperties); - handleNewUpstreamNetworkState(ns); + chooseUpstreamType(false); break; case UpstreamNetworkMonitor.EVENT_ON_LOST: // TODO: Re-evaluate possible upstreams. Currently upstream @@ -1586,7 +1582,7 @@ public class Tethering extends BaseNetworkObserver { if (VDBG) Log.d(TAG, "Tether Mode requested by " + who); handleInterfaceServingStateActive(message.arg1, who); who.sendMessage(TetherInterfaceStateMachine.CMD_TETHER_CONNECTION_CHANGED, - mCurrentUpstreamIface); + mCurrentUpstreamIfaceSet); // If there has been a change and an upstream is now // desired, kick off the selection process. final boolean previousUpstreamWanted = updateUpstreamWanted(); @@ -1864,7 +1860,7 @@ public class Tethering extends BaseNetworkObserver { pw.println(" - lastError = " + tetherState.lastError); } pw.println("Upstream wanted: " + upstreamWanted()); - pw.println("Current upstream interface: " + mCurrentUpstreamIface); + pw.println("Current upstream interface(s): " + mCurrentUpstreamIfaceSet); pw.decreaseIndent(); } @@ -1997,7 +1993,7 @@ public class Tethering extends BaseNetworkObserver { final TetherState tetherState = new TetherState( new TetherInterfaceStateMachine( iface, mLooper, interfaceType, mLog, mNMService, mStatsService, - makeControlCallback(iface))); + makeControlCallback(iface), mDeps)); mTetherStates.put(iface, tetherState); tetherState.stateMachine.start(); } diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java index c9bdcf1a73b5..2fda08e2a575 100644 --- a/services/core/java/com/android/server/connectivity/Vpn.java +++ b/services/core/java/com/android/server/connectivity/Vpn.java @@ -151,6 +151,13 @@ public class Vpn { .multiply(BigInteger.valueOf(howManyPercentIsMost)) .divide(BigInteger.valueOf(100)); } + // How many routes to evaluate before bailing and declaring this Vpn should provide + // the INTERNET capability. This is necessary because computing the adress space is + // O(n²) and this is running in the system service, so a limit is needed to alleviate + // the risk of attack. + // This is taken as a total of IPv4 + IPV6 routes for simplicity, but the algorithm + // is actually O(n²)+O(n²). + private static final int MAX_ROUTES_TO_EVALUATE = 150; // TODO: create separate trackers for each unique VPN to support // automated reconnection @@ -862,10 +869,12 @@ public class Vpn { */ @VisibleForTesting static boolean providesRoutesToMostDestinations(LinkProperties lp) { + final List<RouteInfo> routes = lp.getAllRoutes(); + if (routes.size() > MAX_ROUTES_TO_EVALUATE) return true; final Comparator<IpPrefix> prefixLengthComparator = IpPrefix.lengthComparator(); TreeSet<IpPrefix> ipv4Prefixes = new TreeSet<>(prefixLengthComparator); TreeSet<IpPrefix> ipv6Prefixes = new TreeSet<>(prefixLengthComparator); - for (final RouteInfo route : lp.getAllRoutes()) { + for (final RouteInfo route : routes) { IpPrefix destination = route.getDestination(); if (destination.isIPv4()) { ipv4Prefixes.add(destination); diff --git a/services/core/java/com/android/server/connectivity/tethering/IPv6TetheringCoordinator.java b/services/core/java/com/android/server/connectivity/tethering/IPv6TetheringCoordinator.java index 518f6c1440cc..ba67c94d1cd4 100644 --- a/services/core/java/com/android/server/connectivity/tethering/IPv6TetheringCoordinator.java +++ b/services/core/java/com/android/server/connectivity/tethering/IPv6TetheringCoordinator.java @@ -30,10 +30,8 @@ import android.util.Log; import java.net.Inet6Address; import java.net.InetAddress; -import java.net.UnknownHostException; import java.util.ArrayList; import java.util.Arrays; -import java.util.HashMap; import java.util.LinkedList; import java.util.Random; @@ -119,7 +117,7 @@ public class IPv6TetheringCoordinator { if (VDBG) { Log.d(TAG, "updateUpstreamNetworkState: " + toDebugString(ns)); } - if (!canTetherIPv6(ns, mLog)) { + if (TetheringInterfaceUtils.getIPv6Interface(ns) == null) { stopIPv6TetheringOnAllInterfaces(); setUpstreamNetworkState(null); return; @@ -208,70 +206,6 @@ public class IPv6TetheringCoordinator { return null; } - private static boolean canTetherIPv6(NetworkState ns, SharedLog sharedLog) { - // Broadly speaking: - // - // [1] does the upstream have an IPv6 default route? - // - // and - // - // [2] does the upstream have one or more global IPv6 /64s - // dedicated to this device? - // - // In lieu of Prefix Delegation and other evaluation of whether a - // prefix may or may not be dedicated to this device, for now just - // check whether the upstream is TRANSPORT_CELLULAR. This works - // because "[t]he 3GPP network allocates each default bearer a unique - // /64 prefix", per RFC 6459, Section 5.2. - - final boolean canTether = - (ns != null) && (ns.network != null) && - (ns.linkProperties != null) && (ns.networkCapabilities != null) && - // At least one upstream DNS server: - ns.linkProperties.isProvisioned() && - // Minimal amount of IPv6 provisioning: - ns.linkProperties.hasIPv6DefaultRoute() && - ns.linkProperties.hasGlobalIPv6Address() && - // Temporary approximation of "dedicated prefix": - ns.networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR); - - // For now, we do not support separate IPv4 and IPv6 upstreams (e.g. - // tethering with 464xlat involved). TODO: Rectify this shortcoming, - // likely by calling NetworkManagementService#startInterfaceForwarding() - // for all upstream interfaces. - RouteInfo v4default = null; - RouteInfo v6default = null; - if (canTether) { - for (RouteInfo r : ns.linkProperties.getAllRoutes()) { - if (r.isIPv4Default()) { - v4default = r; - } else if (r.isIPv6Default()) { - v6default = r; - } - - if (v4default != null && v6default != null) { - break; - } - } - } - - final boolean supportedConfiguration = - (v4default != null) && (v6default != null) && - (v4default.getInterface() != null) && - v4default.getInterface().equals(v6default.getInterface()); - - final boolean outcome = canTether && supportedConfiguration; - - if (ns == null) { - sharedLog.log("No available upstream."); - } else { - sharedLog.log(String.format("IPv6 tethering is %s for upstream: %s", - (outcome ? "available" : "not available"), toDebugString(ns))); - } - - return outcome; - } - private static LinkProperties getIPv6OnlyLinkProperties(LinkProperties lp) { final LinkProperties v6only = new LinkProperties(); if (lp == null) { diff --git a/services/core/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachine.java b/services/core/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachine.java index 2224913b2cf6..5ed14a07cb6c 100644 --- a/services/core/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachine.java +++ b/services/core/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachine.java @@ -31,7 +31,7 @@ import android.net.ip.InterfaceController; import android.net.ip.RouterAdvertisementDaemon; import android.net.ip.RouterAdvertisementDaemon.RaParams; import android.net.util.InterfaceParams; -import android.net.util.NetdService; +import android.net.util.InterfaceSet; import android.net.util.SharedLog; import android.os.INetworkManagementService; import android.os.Looper; @@ -49,12 +49,12 @@ import com.android.internal.util.StateMachine; import java.net.Inet6Address; import java.net.InetAddress; -import java.net.SocketException; import java.net.UnknownHostException; import java.util.ArrayList; import java.util.HashSet; import java.util.Objects; import java.util.Random; +import java.util.Set; /** * Provides the interface to IP-layer serving functionality for a given network @@ -117,9 +117,11 @@ public class TetherInterfaceStateMachine extends StateMachine { private final int mInterfaceType; private final LinkProperties mLinkProperties; + private final TetheringDependencies mDeps; + private int mLastError; private int mServingMode; - private String mMyUpstreamIfaceName; // may change over time + private InterfaceSet mUpstreamIfaceSet; // may change over time private InterfaceParams mInterfaceParams; // TODO: De-duplicate this with mLinkProperties above. Currently, these link // properties are those selected by the IPv6TetheringCoordinator and relayed @@ -134,18 +136,19 @@ public class TetherInterfaceStateMachine extends StateMachine { public TetherInterfaceStateMachine( String ifaceName, Looper looper, int interfaceType, SharedLog log, INetworkManagementService nMService, INetworkStatsService statsService, - IControlsTethering tetherController) { + IControlsTethering tetherController, + TetheringDependencies deps) { super(ifaceName, looper); mLog = log.forSubComponent(ifaceName); mNMService = nMService; - // TODO: This should be passed in for testability. - mNetd = NetdService.getInstance(); + mNetd = deps.getNetdService(); mStatsService = statsService; mTetherController = tetherController; mInterfaceCtrl = new InterfaceController(ifaceName, nMService, mNetd, mLog); mIfaceName = ifaceName; mInterfaceType = interfaceType; mLinkProperties = new LinkProperties(); + mDeps = deps; resetLinkProperties(); mLastError = ConnectivityManager.TETHER_ERROR_NO_ERROR; mServingMode = IControlsTethering.STATE_AVAILABLE; @@ -246,16 +249,14 @@ public class TetherInterfaceStateMachine extends StateMachine { } private boolean startIPv6() { - // TODO: Refactor for better testability. This is one of the things - // that prohibits unittesting IPv6 tethering setup. - mInterfaceParams = InterfaceParams.getByName(mIfaceName); + mInterfaceParams = mDeps.getInterfaceParams(mIfaceName); if (mInterfaceParams == null) { mLog.e("Failed to find InterfaceParams"); stopIPv6(); return false; } - mRaDaemon = new RouterAdvertisementDaemon(mInterfaceParams); + mRaDaemon = mDeps.getRouterAdvertisementDaemon(mInterfaceParams); if (!mRaDaemon.start()) { stopIPv6(); return false; @@ -621,10 +622,10 @@ public class TetherInterfaceStateMachine extends StateMachine { } private void cleanupUpstream() { - if (mMyUpstreamIfaceName == null) return; + if (mUpstreamIfaceSet == null) return; - cleanupUpstreamInterface(mMyUpstreamIfaceName); - mMyUpstreamIfaceName = null; + for (String ifname : mUpstreamIfaceSet.ifnames) cleanupUpstreamInterface(ifname); + mUpstreamIfaceSet = null; } private void cleanupUpstreamInterface(String upstreamIface) { @@ -660,34 +661,66 @@ public class TetherInterfaceStateMachine extends StateMachine { mLog.e("CMD_TETHER_REQUESTED while already tethering."); break; case CMD_TETHER_CONNECTION_CHANGED: - String newUpstreamIfaceName = (String)(message.obj); - if ((mMyUpstreamIfaceName == null && newUpstreamIfaceName == null) || - (mMyUpstreamIfaceName != null && - mMyUpstreamIfaceName.equals(newUpstreamIfaceName))) { + final InterfaceSet newUpstreamIfaceSet = (InterfaceSet) message.obj; + if (noChangeInUpstreamIfaceSet(newUpstreamIfaceSet)) { if (VDBG) Log.d(TAG, "Connection changed noop - dropping"); break; } - cleanupUpstream(); - if (newUpstreamIfaceName != null) { + + if (newUpstreamIfaceSet == null) { + cleanupUpstream(); + break; + } + + for (String removed : upstreamInterfacesRemoved(newUpstreamIfaceSet)) { + cleanupUpstreamInterface(removed); + } + + final Set<String> added = upstreamInterfacesAdd(newUpstreamIfaceSet); + // This makes the call to cleanupUpstream() in the error + // path for any interface neatly cleanup all the interfaces. + mUpstreamIfaceSet = newUpstreamIfaceSet; + + for (String ifname : added) { try { - mNMService.enableNat(mIfaceName, newUpstreamIfaceName); - mNMService.startInterfaceForwarding(mIfaceName, - newUpstreamIfaceName); + mNMService.enableNat(mIfaceName, ifname); + mNMService.startInterfaceForwarding(mIfaceName, ifname); } catch (Exception e) { mLog.e("Exception enabling NAT: " + e); - cleanupUpstreamInterface(newUpstreamIfaceName); + cleanupUpstream(); mLastError = ConnectivityManager.TETHER_ERROR_ENABLE_NAT_ERROR; transitionTo(mInitialState); return true; } } - mMyUpstreamIfaceName = newUpstreamIfaceName; break; default: return false; } return true; } + + private boolean noChangeInUpstreamIfaceSet(InterfaceSet newIfaces) { + if (mUpstreamIfaceSet == null && newIfaces == null) return true; + if (mUpstreamIfaceSet != null && newIfaces != null) { + return mUpstreamIfaceSet.equals(newIfaces); + } + return false; + } + + private Set<String> upstreamInterfacesRemoved(InterfaceSet newIfaces) { + if (mUpstreamIfaceSet == null) return new HashSet<>(); + + final HashSet<String> removed = new HashSet<>(mUpstreamIfaceSet.ifnames); + removed.removeAll(newIfaces.ifnames); + return removed; + } + + private Set<String> upstreamInterfacesAdd(InterfaceSet newIfaces) { + final HashSet<String> added = new HashSet<>(newIfaces.ifnames); + if (mUpstreamIfaceSet != null) added.removeAll(mUpstreamIfaceSet.ifnames); + return added; + } } /** diff --git a/services/core/java/com/android/server/connectivity/tethering/TetheringDependencies.java b/services/core/java/com/android/server/connectivity/tethering/TetheringDependencies.java index b8174b6c8d2b..66afb0f2cb14 100644 --- a/services/core/java/com/android/server/connectivity/tethering/TetheringDependencies.java +++ b/services/core/java/com/android/server/connectivity/tethering/TetheringDependencies.java @@ -16,9 +16,18 @@ package com.android.server.connectivity.tethering; +import android.content.Context; +import android.net.INetd; +import android.net.ip.RouterAdvertisementDaemon; +import android.net.util.InterfaceParams; +import android.net.util.NetdService; import android.os.Handler; import android.net.util.SharedLog; +import com.android.internal.util.StateMachine; + +import java.util.ArrayList; + /** * Capture tethering dependencies, for injection. @@ -29,4 +38,26 @@ public class TetheringDependencies { public OffloadHardwareInterface getOffloadHardwareInterface(Handler h, SharedLog log) { return new OffloadHardwareInterface(h, log); } + + public UpstreamNetworkMonitor getUpstreamNetworkMonitor(Context ctx, StateMachine target, + SharedLog log, int what) { + return new UpstreamNetworkMonitor(ctx, target, log, what); + } + + public IPv6TetheringCoordinator getIPv6TetheringCoordinator( + ArrayList<TetherInterfaceStateMachine> notifyList, SharedLog log) { + return new IPv6TetheringCoordinator(notifyList, log); + } + + public RouterAdvertisementDaemon getRouterAdvertisementDaemon(InterfaceParams ifParams) { + return new RouterAdvertisementDaemon(ifParams); + } + + public InterfaceParams getInterfaceParams(String ifName) { + return InterfaceParams.getByName(ifName); + } + + public INetd getNetdService() { + return NetdService.getInstance(); + } } diff --git a/services/core/java/com/android/server/connectivity/tethering/TetheringInterfaceUtils.java b/services/core/java/com/android/server/connectivity/tethering/TetheringInterfaceUtils.java new file mode 100644 index 000000000000..6c7ff91761ac --- /dev/null +++ b/services/core/java/com/android/server/connectivity/tethering/TetheringInterfaceUtils.java @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.connectivity.tethering; + +import android.annotation.Nullable; +import android.net.LinkProperties; +import android.net.NetworkCapabilities; +import android.net.NetworkState; +import android.net.RouteInfo; +import android.net.util.InterfaceSet; + +import java.net.Inet4Address; +import java.net.Inet6Address; +import java.net.InetAddress; + +/** + * @hide + */ +public final class TetheringInterfaceUtils { + /** + * Get upstream interfaces for tethering based on default routes for IPv4/IPv6. + * @return null if there is no usable interface, or a set of at least one interface otherwise. + */ + public static @Nullable InterfaceSet getTetheringInterfaces(NetworkState ns) { + if (ns == null) { + return null; + } + + final LinkProperties lp = ns.linkProperties; + final String if4 = getInterfaceForDestination(lp, Inet4Address.ANY); + final String if6 = getIPv6Interface(ns); + + return (if4 == null && if6 == null) ? null : new InterfaceSet(if4, if6); + } + + /** + * Get the upstream interface for IPv6 tethering. + * @return null if there is no usable interface, or the interface name otherwise. + */ + public static @Nullable String getIPv6Interface(NetworkState ns) { + // Broadly speaking: + // + // [1] does the upstream have an IPv6 default route? + // + // and + // + // [2] does the upstream have one or more global IPv6 /64s + // dedicated to this device? + // + // In lieu of Prefix Delegation and other evaluation of whether a + // prefix may or may not be dedicated to this device, for now just + // check whether the upstream is TRANSPORT_CELLULAR. This works + // because "[t]he 3GPP network allocates each default bearer a unique + // /64 prefix", per RFC 6459, Section 5.2. + final boolean canTether = + (ns != null) && (ns.network != null) && + (ns.linkProperties != null) && (ns.networkCapabilities != null) && + // At least one upstream DNS server: + ns.linkProperties.hasIPv6DnsServer() && + // Minimal amount of IPv6 provisioning: + ns.linkProperties.hasGlobalIPv6Address() && + // Temporary approximation of "dedicated prefix": + ns.networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR); + + return canTether + ? getInterfaceForDestination(ns.linkProperties, Inet6Address.ANY) + : null; + } + + private static String getInterfaceForDestination(LinkProperties lp, InetAddress dst) { + final RouteInfo ri = (lp != null) + ? RouteInfo.selectBestRoute(lp.getAllRoutes(), dst) + : null; + return (ri != null) ? ri.getInterface() : null; + } +} diff --git a/services/core/java/com/android/server/job/controllers/IdleController.java b/services/core/java/com/android/server/job/controllers/IdleController.java index 1dbcfd6a5bdf..40d2a3a4a094 100644 --- a/services/core/java/com/android/server/job/controllers/IdleController.java +++ b/services/core/java/com/android/server/job/controllers/IdleController.java @@ -19,7 +19,6 @@ package com.android.server.job.controllers; import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock; import android.app.AlarmManager; -import android.app.PendingIntent; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; @@ -101,18 +100,19 @@ public final class IdleController extends StateController { final class IdlenessTracker extends BroadcastReceiver { private AlarmManager mAlarm; - private PendingIntent mIdleTriggerIntent; - boolean mIdle; - boolean mScreenOn; + + // After construction, mutations of idle/screen-on state will only happen + // on the main looper thread, either in onReceive() or in an alarm callback. + private boolean mIdle; + private boolean mScreenOn; + + private AlarmManager.OnAlarmListener mIdleAlarmListener = () -> { + handleIdleTrigger(); + }; public IdlenessTracker() { mAlarm = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE); - Intent intent = new Intent(ActivityManagerService.ACTION_TRIGGER_IDLE) - .setPackage("android") - .setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); - mIdleTriggerIntent = PendingIntent.getBroadcast(mContext, 0, intent, 0); - // At boot we presume that the user has just "interacted" with the // device in some meaningful way. mIdle = false; @@ -150,7 +150,7 @@ public final class IdleController extends StateController { } mScreenOn = true; //cancel the alarm - mAlarm.cancel(mIdleTriggerIntent); + mAlarm.cancel(mIdleAlarmListener); if (mIdle) { // possible transition to not-idle mIdle = false; @@ -169,20 +169,24 @@ public final class IdleController extends StateController { } mScreenOn = false; mAlarm.setWindow(AlarmManager.ELAPSED_REALTIME_WAKEUP, - when, mIdleWindowSlop, mIdleTriggerIntent); + when, mIdleWindowSlop, "JS idleness", mIdleAlarmListener, null); } else if (action.equals(ActivityManagerService.ACTION_TRIGGER_IDLE)) { - // idle time starts now. Do not set mIdle if screen is on. - if (!mIdle && !mScreenOn) { - if (DEBUG) { - Slog.v(TAG, "Idle trigger fired @ " + sElapsedRealtimeClock.millis()); - } - mIdle = true; - reportNewIdleState(mIdle); - } else { - if (DEBUG) { - Slog.v(TAG, "TRIGGER_IDLE received but not changing state; idle=" - + mIdle + " screen=" + mScreenOn); - } + handleIdleTrigger(); + } + } + + private void handleIdleTrigger() { + // idle time starts now. Do not set mIdle if screen is on. + if (!mIdle && !mScreenOn) { + if (DEBUG) { + Slog.v(TAG, "Idle trigger fired @ " + sElapsedRealtimeClock.millis()); + } + mIdle = true; + reportNewIdleState(mIdle); + } else { + if (DEBUG) { + Slog.v(TAG, "TRIGGER_IDLE received but not changing state; idle=" + + mIdle + " screen=" + mScreenOn); } } } diff --git a/services/core/java/com/android/server/location/GnssLocationProvider.java b/services/core/java/com/android/server/location/GnssLocationProvider.java index c445f73e5042..5955c9c3dd0a 100644 --- a/services/core/java/com/android/server/location/GnssLocationProvider.java +++ b/services/core/java/com/android/server/location/GnssLocationProvider.java @@ -2513,6 +2513,8 @@ public class GnssLocationProvider implements LocationProviderInterface { * this handler. */ private void handleInitialize() { + native_init_once(); + /* * A cycle of native_init() and native_cleanup() is needed so that callbacks are * registered after bootup even when location is disabled. @@ -2899,6 +2901,8 @@ public class GnssLocationProvider implements LocationProviderInterface { private static native boolean native_is_gnss_configuration_supported(); + private static native void native_init_once(); + private native boolean native_init(); private native void native_cleanup(); diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java index f617964481bf..4b58d537e844 100644 --- a/services/core/java/com/android/server/locksettings/LockSettingsService.java +++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java @@ -2079,11 +2079,6 @@ public class LockSettingsService extends ILockSettings.Stub { } @Override - public byte[] generateAndStoreKey(@NonNull String alias) throws RemoteException { - return mRecoverableKeyStoreManager.generateAndStoreKey(alias); - } - - @Override public @Nullable String generateKey(@NonNull String alias) throws RemoteException { return mRecoverableKeyStoreManager.generateKey(alias); } diff --git a/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java b/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java index 1dab5920288b..ff4c6782accb 100644 --- a/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java +++ b/services/core/java/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManager.java @@ -672,40 +672,6 @@ public class RecoverableKeyStoreManager { } /** - * Deprecated - * Generates a key named {@code alias} in the recoverable store for the calling uid. Then - * returns the raw key material. - * - * <p>TODO: Once AndroidKeyStore has added move api, do not return raw bytes. - * - * @deprecated - * @hide - */ - public byte[] generateAndStoreKey(@NonNull String alias) throws RemoteException { - checkRecoverKeyStorePermission(); - int uid = Binder.getCallingUid(); - int userId = UserHandle.getCallingUserId(); - - PlatformEncryptionKey encryptionKey; - try { - encryptionKey = mPlatformKeyManager.getEncryptKey(userId); - } catch (NoSuchAlgorithmException e) { - // Impossible: all algorithms must be supported by AOSP - throw new RuntimeException(e); - } catch (KeyStoreException | UnrecoverableKeyException e) { - throw new ServiceSpecificException(ERROR_SERVICE_INTERNAL_ERROR, e.getMessage()); - } catch (InsecureUserException e) { - throw new ServiceSpecificException(ERROR_INSECURE_USER, e.getMessage()); - } - - try { - return mRecoverableKeyGenerator.generateAndStoreKey(encryptionKey, userId, uid, alias); - } catch (KeyStoreException | InvalidKeyException | RecoverableKeyStorageException e) { - throw new ServiceSpecificException(ERROR_SERVICE_INTERNAL_ERROR, e.getMessage()); - } - } - - /** * Destroys the session with the given {@code sessionId}. */ public void closeSession(@NonNull String sessionId) throws RemoteException { diff --git a/services/core/java/com/android/server/notification/ManagedServices.java b/services/core/java/com/android/server/notification/ManagedServices.java index b7842d5365b5..d5a32aa30936 100644 --- a/services/core/java/com/android/server/notification/ManagedServices.java +++ b/services/core/java/com/android/server/notification/ManagedServices.java @@ -982,7 +982,11 @@ abstract public class ManagedServices { Slog.w(TAG, getCaption() + " binding died: " + name); synchronized (mMutex) { mServicesBinding.remove(servicesBindingTag); - mContext.unbindService(this); + try { + mContext.unbindService(this); + } catch (IllegalArgumentException e) { + Slog.e(TAG, "failed to unbind " + name, e); + } if (!mServicesRebinding.contains(servicesBindingTag)) { mServicesRebinding.add(servicesBindingTag); mHandler.postDelayed(new Runnable() { diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index 31b0461b53b4..f31ca0a2fa09 100644 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -182,6 +182,7 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; +import com.android.internal.notification.SystemNotificationChannels; import com.android.internal.os.BackgroundThread; import com.android.internal.statusbar.NotificationVisibility; import com.android.internal.util.ArrayUtils; @@ -898,6 +899,8 @@ public class NotificationManagerService extends SystemService { @Override public void onReceive(Context context, Intent intent) { if (Intent.ACTION_LOCALE_CHANGED.equals(intent.getAction())) { + // update system notification channels + SystemNotificationChannels.createAll(context); mZenModeHelper.updateDefaultZenRules(); mRankingHelper.onLocaleChanged(context, ActivityManager.getCurrentUser()); } diff --git a/services/core/java/com/android/server/pm/Installer.java b/services/core/java/com/android/server/pm/Installer.java index 24abf8657b88..9d3f48b428ce 100644 --- a/services/core/java/com/android/server/pm/Installer.java +++ b/services/core/java/com/android/server/pm/Installer.java @@ -484,11 +484,11 @@ public class Installer extends SystemService { } } - public void installApkVerity(String filePath, FileDescriptor verityInput) + public void installApkVerity(String filePath, FileDescriptor verityInput, int contentSize) throws InstallerException { if (!checkBeforeRemote()) return; try { - mInstalld.installApkVerity(filePath, verityInput); + mInstalld.installApkVerity(filePath, verityInput, contentSize); } catch (Exception e) { throw InstallerException.from(e); } diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 7b77c9660dae..cb1972f17515 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -17322,8 +17322,11 @@ public class PackageManagerService extends IPackageManager.Stub if (Build.IS_DEBUGGABLE) Slog.i(TAG, "Enabling apk verity to " + apkPath); FileDescriptor fd = result.getUnownedFileDescriptor(); try { - mInstaller.installApkVerity(apkPath, fd); - } catch (InstallerException e) { + final byte[] signedRootHash = VerityUtils.generateFsverityRootHash(apkPath); + mInstaller.installApkVerity(apkPath, fd, result.getContentSize()); + mInstaller.assertFsverityRootHashMatches(apkPath, signedRootHash); + } catch (InstallerException | IOException | DigestException | + NoSuchAlgorithmException e) { res.setError(INSTALL_FAILED_INTERNAL_ERROR, "Failed to set up verity: " + e); return; diff --git a/services/core/java/com/android/server/policy/keyguard/KeyguardStateMonitor.java b/services/core/java/com/android/server/policy/keyguard/KeyguardStateMonitor.java index 941cd4441e23..e56caf849aac 100644 --- a/services/core/java/com/android/server/policy/keyguard/KeyguardStateMonitor.java +++ b/services/core/java/com/android/server/policy/keyguard/KeyguardStateMonitor.java @@ -19,6 +19,8 @@ package com.android.server.policy.keyguard; import android.app.ActivityManager; import android.content.Context; import android.os.RemoteException; +import android.os.ServiceManager; +import android.security.IKeystoreService; import android.util.Slog; import com.android.internal.policy.IKeyguardService; @@ -51,11 +53,16 @@ public class KeyguardStateMonitor extends IKeyguardStateCallback.Stub { private final LockPatternUtils mLockPatternUtils; private final StateCallback mCallback; + IKeystoreService mKeystoreService; + public KeyguardStateMonitor(Context context, IKeyguardService service, StateCallback callback) { mLockPatternUtils = new LockPatternUtils(context); mCurrentUserId = ActivityManager.getCurrentUser(); mCallback = callback; + mKeystoreService = IKeystoreService.Stub.asInterface(ServiceManager + .getService("android.security.keystore")); + try { service.addStateMonitorCallback(this); } catch (RemoteException e) { @@ -86,6 +93,12 @@ public class KeyguardStateMonitor extends IKeyguardStateCallback.Stub { @Override // Binder interface public void onShowingStateChanged(boolean showing) { mIsShowing = showing; + + try { + mKeystoreService.onKeyguardVisibilityChanged(showing, mCurrentUserId); + } catch (RemoteException e) { + Slog.e(TAG, "Error informing keystore of screen lock", e); + } } @Override // Binder interface @@ -130,4 +143,4 @@ public class KeyguardStateMonitor extends IKeyguardStateCallback.Stub { pw.println(prefix + "mTrusted=" + mTrusted); pw.println(prefix + "mCurrentUserId=" + mCurrentUserId); } -}
\ No newline at end of file +} diff --git a/services/core/java/com/android/server/security/VerityUtils.java b/services/core/java/com/android/server/security/VerityUtils.java index d2d0e60dc742..180f34355c94 100644 --- a/services/core/java/com/android/server/security/VerityUtils.java +++ b/services/core/java/com/android/server/security/VerityUtils.java @@ -26,6 +26,7 @@ import android.system.Os; import android.util.apk.ApkSignatureVerifier; import android.util.apk.ByteBufferFactory; import android.util.apk.SignatureNotFoundException; +import android.util.Pair; import android.util.Slog; import java.io.FileDescriptor; @@ -59,12 +60,15 @@ abstract public class VerityUtils { return SetupResult.skipped(); } - shm = generateApkVerityIntoSharedMemory(apkPath, signedRootHash); + Pair<SharedMemory, Integer> result = generateApkVerityIntoSharedMemory(apkPath, + signedRootHash); + shm = result.first; + int contentSize = result.second; FileDescriptor rfd = shm.getFileDescriptor(); if (rfd == null || !rfd.valid()) { return SetupResult.failed(); } - return SetupResult.ok(Os.dup(rfd)); + return SetupResult.ok(Os.dup(rfd), contentSize); } catch (IOException | SecurityException | DigestException | NoSuchAlgorithmException | SignatureNotFoundException | ErrnoException e) { Slog.e(TAG, "Failed to set up apk verity: ", e); @@ -85,10 +89,20 @@ abstract public class VerityUtils { } /** - * Returns a {@code SharedMemory} that contains Merkle tree and fsverity headers for the given - * apk, in the form that can immediately be used for fsverity setup. + * {@see ApkSignatureVerifier#getVerityRootHash(String)}. */ - private static SharedMemory generateApkVerityIntoSharedMemory( + public static byte[] getVerityRootHash(@NonNull String apkPath) + throws IOException, SignatureNotFoundException, SecurityException { + return ApkSignatureVerifier.getVerityRootHash(apkPath); + } + + /** + * Returns a pair of {@code SharedMemory} and {@code Integer}. The {@code SharedMemory} contains + * Merkle tree and fsverity headers for the given apk, in the form that can immediately be used + * for fsverity setup. The data is aligned to the beginning of {@code SharedMemory}, and has + * length equals to the returned {@code Integer}. + */ + private static Pair<SharedMemory, Integer> generateApkVerityIntoSharedMemory( String apkPath, byte[] expectedRootHash) throws IOException, SecurityException, DigestException, NoSuchAlgorithmException, SignatureNotFoundException { @@ -101,6 +115,7 @@ abstract public class VerityUtils { throw new SecurityException("Locally generated verity root hash does not match"); } + int contentSize = shmBufferFactory.getBufferLimit(); SharedMemory shm = shmBufferFactory.releaseSharedMemory(); if (shm == null) { throw new IllegalStateException("Failed to generate verity tree into shared memory"); @@ -108,7 +123,7 @@ abstract public class VerityUtils { if (!shm.setProtect(PROT_READ)) { throw new SecurityException("Failed to set up shared memory correctly"); } - return shm; + return Pair.create(shm, contentSize); } public static class SetupResult { @@ -123,22 +138,24 @@ abstract public class VerityUtils { private final int mCode; private final FileDescriptor mFileDescriptor; + private final int mContentSize; - public static SetupResult ok(@NonNull FileDescriptor fileDescriptor) { - return new SetupResult(RESULT_OK, fileDescriptor); + public static SetupResult ok(@NonNull FileDescriptor fileDescriptor, int contentSize) { + return new SetupResult(RESULT_OK, fileDescriptor, contentSize); } public static SetupResult skipped() { - return new SetupResult(RESULT_SKIPPED, null); + return new SetupResult(RESULT_SKIPPED, null, -1); } public static SetupResult failed() { - return new SetupResult(RESULT_FAILED, null); + return new SetupResult(RESULT_FAILED, null, -1); } - private SetupResult(int code, FileDescriptor fileDescriptor) { + private SetupResult(int code, FileDescriptor fileDescriptor, int contentSize) { this.mCode = code; this.mFileDescriptor = fileDescriptor; + this.mContentSize = contentSize; } public boolean isFailed() { @@ -152,6 +169,10 @@ abstract public class VerityUtils { public @NonNull FileDescriptor getUnownedFileDescriptor() { return mFileDescriptor; } + + public int getContentSize() { + return mContentSize; + } } /** A {@code ByteBufferFactory} that creates a shared memory backed {@code ByteBuffer}. */ @@ -188,5 +209,9 @@ abstract public class VerityUtils { mShm = null; return tmp; } + + public int getBufferLimit() { + return mBuffer == null ? -1 : mBuffer.limit(); + } } } diff --git a/services/core/java/com/android/server/slice/SliceManagerService.java b/services/core/java/com/android/server/slice/SliceManagerService.java index 0b7d9d02f1cb..fd0b6f1eb457 100644 --- a/services/core/java/com/android/server/slice/SliceManagerService.java +++ b/services/core/java/com/android/server/slice/SliceManagerService.java @@ -168,7 +168,8 @@ public class SliceManagerService extends ISliceManager.Stub { } @Override - public void pinSlice(String pkg, Uri uri, SliceSpec[] specs, IBinder token) throws RemoteException { + public void pinSlice(String pkg, Uri uri, SliceSpec[] specs, IBinder token) + throws RemoteException { verifyCaller(pkg); enforceAccess(pkg, uri); int user = Binder.getCallingUserHandle().getIdentifier(); @@ -210,7 +211,8 @@ public class SliceManagerService extends ISliceManager.Stub { } @Override - public int checkSlicePermission(Uri uri, String pkg, int pid, int uid) throws RemoteException { + public int checkSlicePermission(Uri uri, String pkg, int pid, int uid, + String[] autoGrantPermissions) throws RemoteException { if (mContext.checkUriPermission(uri, pid, uid, Intent.FLAG_GRANT_WRITE_URI_PERMISSION) == PERMISSION_GRANTED) { return SliceManager.PERMISSION_GRANTED; @@ -218,6 +220,11 @@ public class SliceManagerService extends ISliceManager.Stub { if (hasFullSliceAccess(pkg, UserHandle.getUserId(uid))) { return SliceManager.PERMISSION_GRANTED; } + for (String perm : autoGrantPermissions) { + if (mContext.checkPermission(perm, pid, uid) == PERMISSION_GRANTED) { + return SliceManager.PERMISSION_USER_GRANTED; + } + } synchronized (mLock) { if (mUserGrants.contains(new SliceGrant(uri, pkg, UserHandle.getUserId(uid)))) { return SliceManager.PERMISSION_USER_GRANTED; diff --git a/services/core/jni/BroadcastRadio/BroadcastRadioService.cpp b/services/core/jni/BroadcastRadio/BroadcastRadioService.cpp index ecf1a33539e9..f7ca363e54a1 100644 --- a/services/core/jni/BroadcastRadio/BroadcastRadioService.cpp +++ b/services/core/jni/BroadcastRadio/BroadcastRadioService.cpp @@ -231,6 +231,13 @@ static jobject nativeOpenTuner(JNIEnv *env, jobject obj, long nativeContext, jin return nullptr; } bandConfigHal = module.bands[0]; + + /* Prefer FM to workaround possible program list fetching limitation + * (if tuner scans only configured band for programs). */ + auto fmIt = std::find_if(module.bands.begin(), module.bands.end(), + [](const BandConfig & band) { return utils::isFm(band.type); }); + if (fmIt != module.bands.end()) bandConfigHal = *fmIt; + if (bandConfigHal.spacings.size() > 1) { bandConfigHal.spacings = hidl_vec<uint32_t>({ *std::min_element( bandConfigHal.spacings.begin(), bandConfigHal.spacings.end()) }); diff --git a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp index dcee15106f8a..21fea1c910cb 100644 --- a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp +++ b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp @@ -1125,6 +1125,16 @@ Return<void> GnssBatchingCallback::gnssLocationBatchCb( } static void android_location_GnssLocationProvider_class_init_native(JNIEnv* env, jclass clazz) { + gnssHal_V1_1 = IGnss_V1_1::getService(); + if (gnssHal_V1_1 == nullptr) { + ALOGD("gnssHal 1.1 was null, trying 1.0"); + gnssHal = IGnss_V1_0::getService(); + } else { + gnssHal = gnssHal_V1_1; + } +} + +static void android_location_GnssLocationProvider_init_once(JNIEnv* env, jclass clazz) { method_reportLocation = env->GetMethodID(clazz, "reportLocation", "(ZLandroid/location/Location;)V"); method_reportStatus = env->GetMethodID(clazz, "reportStatus", "(I)V"); @@ -1175,15 +1185,6 @@ static void android_location_GnssLocationProvider_class_init_native(JNIEnv* env, LOG_ALWAYS_FATAL("Unable to get Java VM. Error: %d", jvmStatus); } - // TODO(b/31632518) - gnssHal_V1_1 = IGnss_V1_1::getService(); - if (gnssHal_V1_1 == nullptr) { - ALOGD("gnssHal 1.1 was null, trying 1.0"); - gnssHal = IGnss_V1_0::getService(); - } else { - gnssHal = gnssHal_V1_1; - } - if (gnssHal != nullptr) { gnssHalDeathRecipient = new GnssDeathRecipient(); hardware::Return<bool> linked = gnssHal->linkToDeath( @@ -2068,6 +2069,8 @@ static const JNINativeMethod sMethods[] = { {"native_is_gnss_configuration_supported", "()Z", reinterpret_cast<void *>( android_location_gpsLocationProvider_is_gnss_configuration_supported)}, + {"native_init_once", "()V", reinterpret_cast<void *>( + android_location_GnssLocationProvider_init_once)}, {"native_init", "()Z", reinterpret_cast<void *>(android_location_GnssLocationProvider_init)}, {"native_cleanup", "()V", reinterpret_cast<void *>( android_location_GnssLocationProvider_cleanup)}, diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java b/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java index 71c2ea18eb1b..1c9782fa5565 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java @@ -78,23 +78,6 @@ abstract class BaseIDevicePolicyManager extends IDevicePolicyManager.Stub { return false; } - @Override - public boolean setPasswordBlacklist(ComponentName who, String name, List<String> blacklist, - boolean parent) { - return false; - } - - @Override - public String getPasswordBlacklistName(ComponentName who, @UserIdInt int userId, - boolean parent) { - return null; - } - - @Override - public boolean isPasswordBlacklisted(@UserIdInt int userId, String password) { - return false; - } - public boolean isUsingUnifiedPassword(ComponentName who) { return true; } diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index ffa7311f607d..90e8a9cd74e1 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -820,7 +820,6 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { private static final String TAG_PASSWORD_HISTORY_LENGTH = "password-history-length"; private static final String TAG_MIN_PASSWORD_LENGTH = "min-password-length"; private static final String ATTR_VALUE = "value"; - private static final String TAG_PASSWORD_BLACKLIST = "password-blacklist"; private static final String TAG_PASSWORD_QUALITY = "password-quality"; private static final String TAG_POLICIES = "policies"; private static final String TAG_CROSS_PROFILE_WIDGET_PROVIDERS = @@ -961,9 +960,6 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { // Default title of confirm credentials screen String organizationName = null; - // The blacklist data is stored in a file whose name is stored in the XML - String passwordBlacklistFile = null; - // The component name of the backup transport which has to be used if backups are mandatory // or null if backups are not mandatory. ComponentName mandatoryBackupTransport = null; @@ -1053,11 +1049,6 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { out.endTag(null, TAG_MIN_PASSWORD_NONLETTER); } } - if (passwordBlacklistFile != null) { - out.startTag(null, TAG_PASSWORD_BLACKLIST); - out.attribute(null, ATTR_VALUE, passwordBlacklistFile); - out.endTag(null, TAG_PASSWORD_BLACKLIST); - } if (maximumTimeToUnlock != DEF_MAXIMUM_TIME_TO_UNLOCK) { out.startTag(null, TAG_MAX_TIME_TO_UNLOCK); out.attribute(null, ATTR_VALUE, Long.toString(maximumTimeToUnlock)); @@ -1313,8 +1304,6 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } else if (TAG_MIN_PASSWORD_NONLETTER.equals(tag)) { minimumPasswordMetrics.nonLetter = Integer.parseInt( parser.getAttributeValue(null, ATTR_VALUE)); - } else if (TAG_PASSWORD_BLACKLIST.equals(tag)) { - passwordBlacklistFile = parser.getAttributeValue(null, ATTR_VALUE); }else if (TAG_MAX_TIME_TO_UNLOCK.equals(tag)) { maximumTimeToUnlock = Long.parseLong( parser.getAttributeValue(null, ATTR_VALUE)); @@ -1589,8 +1578,6 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { pw.println(minimumPasswordMetrics.symbols); pw.print(prefix); pw.print("minimumPasswordNonLetter="); pw.println(minimumPasswordMetrics.nonLetter); - pw.print(prefix); pw.print("passwordBlacklist="); - pw.println(passwordBlacklistFile != null); pw.print(prefix); pw.print("maximumTimeToUnlock="); pw.println(maximumTimeToUnlock); pw.print(prefix); pw.print("strongAuthUnlockTimeout="); @@ -1857,10 +1844,6 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { return new LockPatternUtils(mContext); } - PasswordBlacklist newPasswordBlacklist(File file) { - return new PasswordBlacklist(file); - } - boolean storageManagerIsFileBasedEncryptionEnabled() { return StorageManager.isFileEncryptedNativeOnly(); } @@ -4413,136 +4396,6 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { } } - /* @return the password blacklist set by the admin or {@code null} if none. */ - PasswordBlacklist getAdminPasswordBlacklistLocked(@NonNull ActiveAdmin admin) { - final int userId = UserHandle.getUserId(admin.getUid()); - return admin.passwordBlacklistFile == null ? null : new PasswordBlacklist( - new File(getPolicyFileDirectory(userId), admin.passwordBlacklistFile)); - } - - private static final String PASSWORD_BLACKLIST_FILE_PREFIX = "password-blacklist-"; - private static final String PASSWORD_BLACKLIST_FILE_SUFFIX = ""; - - @Override - public boolean setPasswordBlacklist(ComponentName who, String name, List<String> blacklist, - boolean parent) { - if (!mHasFeature) { - return false; - } - Preconditions.checkNotNull(who, "who is null"); - - synchronized (this) { - final ActiveAdmin admin = getActiveAdminForCallerLocked( - who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER, parent); - final int userId = mInjector.userHandleGetCallingUserId(); - PasswordBlacklist adminBlacklist = getAdminPasswordBlacklistLocked(admin); - - if (blacklist == null || blacklist.isEmpty()) { - // Remove the adminBlacklist - admin.passwordBlacklistFile = null; - saveSettingsLocked(userId); - if (adminBlacklist != null) { - adminBlacklist.delete(); - } - return true; - } - - // Validate server side - Preconditions.checkNotNull(name, "name is null"); - DevicePolicyManager.enforcePasswordBlacklistSize(blacklist); - - // Blacklist is case insensitive so normalize to lower case - final int blacklistSize = blacklist.size(); - for (int i = 0; i < blacklistSize; ++i) { - blacklist.set(i, blacklist.get(i).toLowerCase()); - } - - final boolean isNewBlacklist = adminBlacklist == null; - if (isNewBlacklist) { - // Create a new file for the blacklist. There could be multiple admins, each setting - // different blacklists, to restrict a user's credential, for example a managed - // profile can impose restrictions on its parent while the parent is already - // restricted by its own admin. A deterministic naming scheme would be fragile if - // new types of admin are introduced so we generate and save the file name instead. - // This isn't a temporary file but it reuses the name generation logic - final File file; - try { - file = File.createTempFile(PASSWORD_BLACKLIST_FILE_PREFIX, - PASSWORD_BLACKLIST_FILE_SUFFIX, getPolicyFileDirectory(userId)); - } catch (IOException e) { - Slog.e(LOG_TAG, "Failed to make a file for the blacklist", e); - return false; - } - adminBlacklist = mInjector.newPasswordBlacklist(file); - } - - if (adminBlacklist.savePasswordBlacklist(name, blacklist)) { - if (isNewBlacklist) { - // The blacklist was saved so point the admin to the file - admin.passwordBlacklistFile = adminBlacklist.getFile().getName(); - saveSettingsLocked(userId); - } - return true; - } - } - - return false; - } - - @Override - public String getPasswordBlacklistName(ComponentName who, @UserIdInt int userId, - boolean parent) { - if (!mHasFeature) { - return null; - } - Preconditions.checkNotNull(who, "who is null"); - enforceFullCrossUsersPermission(userId); - synchronized (this) { - final ActiveAdmin admin = getActiveAdminForCallerLocked( - who, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER, parent); - final PasswordBlacklist blacklist = getAdminPasswordBlacklistLocked(admin); - if (blacklist == null) { - return null; - } - return blacklist.getName(); - } - } - - @Override - public boolean isPasswordBlacklisted(@UserIdInt int userId, String password) { - if (!mHasFeature) { - return false; - } - mContext.enforceCallingOrSelfPermission( - android.Manifest.permission.TEST_BLACKLISTED_PASSWORD, null); - return isPasswordBlacklistedInternal(userId, password); - } - - private boolean isPasswordBlacklistedInternal(@UserIdInt int userId, String password) { - Preconditions.checkNotNull(password, "Password is null"); - enforceFullCrossUsersPermission(userId); - - // Normalize to lower case for case insensitive blacklist match - final String lowerCasePassword = password.toLowerCase(); - - synchronized (this) { - final List<ActiveAdmin> admins = - getActiveAdminsForLockscreenPoliciesLocked(userId, /* parent */ false); - final int N = admins.size(); - for (int i = 0; i < N; i++) { - final PasswordBlacklist blacklist - = getAdminPasswordBlacklistLocked(admins.get(i)); - if (blacklist != null) { - if (blacklist.isPasswordBlacklisted(lowerCasePassword)) { - return true; - } - } - } - } - - return false; - } - @Override public boolean isActivePasswordSufficient(int userHandle, boolean parent) { if (!mHasFeature) { @@ -4938,11 +4791,6 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { return false; } } - - if (isPasswordBlacklistedInternal(userHandle, password)) { - Slog.w(LOG_TAG, "resetPassword: the password is blacklisted"); - return false; - } } DevicePolicyData policy = getUserData(userHandle); diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/PasswordBlacklist.java b/services/devicepolicy/java/com/android/server/devicepolicy/PasswordBlacklist.java deleted file mode 100644 index a17a1075f100..000000000000 --- a/services/devicepolicy/java/com/android/server/devicepolicy/PasswordBlacklist.java +++ /dev/null @@ -1,165 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.devicepolicy; - -import android.annotation.NonNull; -import android.annotation.Nullable; -import android.util.AtomicFile; -import android.util.Slog; - -import java.io.BufferedInputStream; -import java.io.BufferedOutputStream; -import java.io.DataInputStream; -import java.io.DataOutputStream; -import java.io.File; -import java.io.FileOutputStream; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.util.List; - -/** - * Manages the blacklisted passwords. - * - * This caller must ensure synchronized access. - */ -public class PasswordBlacklist { - private static final String TAG = "PasswordBlacklist"; - - private final AtomicFile mFile; - - /** - * Create an object to manage the password blacklist. - * - * This is a lightweight operation to prepare variables but not perform any IO. - */ - public PasswordBlacklist(File file) { - mFile = new AtomicFile(file, "device-policy"); - } - - /** - * Atomically replace the blacklist. - * - * Pass {@code null} for an empty list. - */ - public boolean savePasswordBlacklist(@NonNull String name, @NonNull List<String> blacklist) { - FileOutputStream fos = null; - try { - fos = mFile.startWrite(); - final DataOutputStream out = buildStreamForWriting(fos); - final Header header = new Header(Header.VERSION_1, name, blacklist.size()); - header.write(out); - final int blacklistSize = blacklist.size(); - for (int i = 0; i < blacklistSize; ++i) { - out.writeUTF(blacklist.get(i)); - } - out.flush(); - mFile.finishWrite(fos); - return true; - } catch (IOException e) { - mFile.failWrite(fos); - return false; - } - } - - /** @return the name of the blacklist or {@code null} if none set. */ - public String getName() { - try (DataInputStream in = openForReading()) { - return Header.read(in).mName; - } catch (IOException e) { - Slog.wtf(TAG, "Failed to read blacklist file", e); - } - return null; - } - - /** @return the number of blacklisted passwords. */ - public int getSize() { - final int blacklistSize; - try (DataInputStream in = openForReading()) { - return Header.read(in).mSize; - } catch (IOException e) { - Slog.wtf(TAG, "Failed to read blacklist file", e); - } - return 0; - } - - /** @return whether the password matches an blacklisted item. */ - public boolean isPasswordBlacklisted(@NonNull String password) { - final int blacklistSize; - try (DataInputStream in = openForReading()) { - final Header header = Header.read(in); - for (int i = 0; i < header.mSize; ++i) { - if (in.readUTF().equals(password)) { - return true; - } - } - } catch (IOException e) { - Slog.wtf(TAG, "Failed to read blacklist file", e); - // Fail safe and block all passwords. Setting a new blacklist should resolve this - // problem which can be identified by examining the log. - return true; - } - return false; - } - - /** Delete the blacklist completely from disk. */ - public void delete() { - mFile.delete(); - } - - /** Get the file the blacklist is stored in. */ - public File getFile() { - return mFile.getBaseFile(); - } - - private DataOutputStream buildStreamForWriting(FileOutputStream fos) { - return new DataOutputStream(new BufferedOutputStream(fos)); - } - - private DataInputStream openForReading() throws IOException { - return new DataInputStream(new BufferedInputStream(mFile.openRead())); - } - - /** - * Helper to read and write the header of the blacklist file. - */ - private static class Header { - static final int VERSION_1 = 1; - - final int mVersion; // File format version - final String mName; - final int mSize; - - Header(int version, String name, int size) { - mVersion = version; - mName = name; - mSize = size; - } - - void write(DataOutputStream out) throws IOException { - out.writeInt(mVersion); - out.writeUTF(mName); - out.writeInt(mSize); - } - - static Header read(DataInputStream in) throws IOException { - final int version = in.readInt(); - final String name = in.readUTF(); - final int size = in.readInt(); - return new Header(version, name, size); - } - } -} diff --git a/services/net/java/android/net/apf/ApfGenerator.java b/services/net/java/android/net/apf/ApfGenerator.java index d41fbce1f206..ca8f72760457 100644 --- a/services/net/java/android/net/apf/ApfGenerator.java +++ b/services/net/java/android/net/apf/ApfGenerator.java @@ -367,7 +367,7 @@ public class ApfGenerator { */ public boolean setApfVersion(int version) { // This version number syncs up with APF_VERSION in hardware/google/apf/apf_interpreter.h - return version == 2; + return version >= 2; } private void addInstruction(Instruction instruction) { diff --git a/services/net/java/android/net/util/InterfaceSet.java b/services/net/java/android/net/util/InterfaceSet.java new file mode 100644 index 000000000000..9f26fa17ef5c --- /dev/null +++ b/services/net/java/android/net/util/InterfaceSet.java @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.net.util; + +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; +import java.util.StringJoiner; + + +/** + * @hide + */ +public class InterfaceSet { + public final Set<String> ifnames; + + public InterfaceSet(String... names) { + final Set<String> nameSet = new HashSet<>(); + for (String name : names) { + if (name != null) nameSet.add(name); + } + ifnames = Collections.unmodifiableSet(nameSet); + } + + @Override + public String toString() { + final StringJoiner sj = new StringJoiner(",", "[", "]"); + for (String ifname : ifnames) sj.add(ifname); + return sj.toString(); + } + + @Override + public boolean equals(Object obj) { + return obj != null + && obj instanceof InterfaceSet + && ifnames.equals(((InterfaceSet)obj).ifnames); + } +} diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java index 520f31879e35..cd3928558c4b 100644 --- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java +++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceTestable.java @@ -98,12 +98,6 @@ public class DevicePolicyManagerServiceTestable extends DevicePolicyManagerServi this.context = injector.context; } - @Override - public boolean isPasswordBlacklisted(int userId, String password) { - return false; - } - - public void notifyChangeToContentObserver(Uri uri, int userHandle) { ContentObserver co = mMockInjector.mContentObservers.get(new Pair<>(uri, userHandle)); if (co != null) { @@ -220,11 +214,6 @@ public class DevicePolicyManagerServiceTestable extends DevicePolicyManagerServi } @Override - PasswordBlacklist newPasswordBlacklist(File file) { - return services.passwordBlacklist; - } - - @Override boolean storageManagerIsFileBasedEncryptionEnabled() { return services.storageManager.isFileBasedEncryptionEnabled(); } diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java index ccf4a8293d67..b76064b7ebdf 100644 --- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java @@ -4192,36 +4192,6 @@ public class DevicePolicyManagerTest extends DpmTestBase { assertTrue(dpm.clearResetPasswordToken(admin1)); } - public void testSetPasswordBlacklistCannotBeCalledByNonAdmin() throws Exception { - assertExpectException(SecurityException.class, /* messageRegex= */ null, - () -> dpm.setPasswordBlacklist(admin1, null, null)); - verifyZeroInteractions(getServices().passwordBlacklist); - } - - public void testClearingPasswordBlacklistDoesNotCreateNewBlacklist() throws Exception { - setupProfileOwner(); - dpm.setPasswordBlacklist(admin1, null, null); - verifyZeroInteractions(getServices().passwordBlacklist); - } - - public void testSetPasswordBlacklistCreatesNewBlacklist() throws Exception { - final String name = "myblacklist"; - final List<String> explicit = Arrays.asList("password", "letmein"); - setupProfileOwner(); - dpm.setPasswordBlacklist(admin1, name, explicit); - verify(getServices().passwordBlacklist).savePasswordBlacklist(name, explicit); - } - - public void testSetPasswordBlacklistOnlyConvertsExplicitToLowerCase() throws Exception { - final List<String> mixedCase = Arrays.asList("password", "LETMEIN", "FooTBAll"); - final List<String> lowerCase = Arrays.asList("password", "letmein", "football"); - mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID; - setupDeviceOwner(); - final String name = "Name of the Blacklist"; - dpm.setPasswordBlacklist(admin1, name, mixedCase); - verify(getServices().passwordBlacklist).savePasswordBlacklist(name, lowerCase); - } - public void testIsActivePasswordSufficient() throws Exception { mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID; mContext.packageName = admin1.getPackageName(); diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java b/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java index 81ed6e2588fd..e753df1f30ec 100644 --- a/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java +++ b/services/tests/servicestests/src/com/android/server/devicepolicy/MockSystemServices.java @@ -96,7 +96,6 @@ public class MockSystemServices { public final IBackupManager ibackupManager; public final IAudioService iaudioService; public final LockPatternUtils lockPatternUtils; - public final PasswordBlacklist passwordBlacklist; public final StorageManagerForMock storageManager; public final WifiManager wifiManager; public final SettingsForMock settings; @@ -135,7 +134,6 @@ public class MockSystemServices { ibackupManager = mock(IBackupManager.class); iaudioService = mock(IAudioService.class); lockPatternUtils = mock(LockPatternUtils.class); - passwordBlacklist = mock(PasswordBlacklist.class); storageManager = mock(StorageManagerForMock.class); wifiManager = mock(WifiManager.class); settings = mock(SettingsForMock.class); diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/PasswordBlacklistTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/PasswordBlacklistTest.java deleted file mode 100644 index 1b3fc2c1f207..000000000000 --- a/services/tests/servicestests/src/com/android/server/devicepolicy/PasswordBlacklistTest.java +++ /dev/null @@ -1,110 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package com.android.server.devicepolicy; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; - -import android.content.Context; -import android.support.test.InstrumentationRegistry; -import android.support.test.runner.AndroidJUnit4; - -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.JUnit4; - -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import java.util.Arrays; -import java.util.ArrayList; -import java.util.List; - -/** - * Unit tests for {@link PasswordBlacklist}. - * - * bit FrameworksServicesTests:com.android.server.devicepolicy.PasswordBlacklistTest - * runtest -x frameworks/base/services/tests/servicestests/src/com/android/server/devicepolicy/PasswordBlacklistTest.java - */ -@RunWith(AndroidJUnit4.class) -public final class PasswordBlacklistTest { - private File mBlacklistFile; - private PasswordBlacklist mBlacklist; - - @Before - public void setUp() throws IOException { - mBlacklistFile = File.createTempFile("pwdbl", null); - mBlacklist = new PasswordBlacklist(mBlacklistFile); - } - - @After - public void tearDown() { - mBlacklist.delete(); - } - - @Test - public void matchIsExact() { - // Note: Case sensitivity is handled by the user of PasswordBlacklist by normalizing the - // values stored in and tested against it. - mBlacklist.savePasswordBlacklist("matchIsExact", Arrays.asList("password", "qWERty")); - assertTrue(mBlacklist.isPasswordBlacklisted("password")); - assertTrue(mBlacklist.isPasswordBlacklisted("qWERty")); - assertFalse(mBlacklist.isPasswordBlacklisted("Password")); - assertFalse(mBlacklist.isPasswordBlacklisted("qwert")); - assertFalse(mBlacklist.isPasswordBlacklisted("letmein")); - } - - @Test - public void matchIsNotRegex() { - mBlacklist.savePasswordBlacklist("matchIsNotRegex", Arrays.asList("a+b*")); - assertTrue(mBlacklist.isPasswordBlacklisted("a+b*")); - assertFalse(mBlacklist.isPasswordBlacklisted("aaaa")); - assertFalse(mBlacklist.isPasswordBlacklisted("abbbb")); - assertFalse(mBlacklist.isPasswordBlacklisted("aaaa")); - } - - @Test - public void matchFailsSafe() throws IOException { - try (FileOutputStream fos = new FileOutputStream(mBlacklistFile)) { - // Write a malformed blacklist file - fos.write(17); - } - assertTrue(mBlacklist.isPasswordBlacklisted("anything")); - assertTrue(mBlacklist.isPasswordBlacklisted("at")); - assertTrue(mBlacklist.isPasswordBlacklisted("ALL")); - } - - @Test - public void blacklistCanBeNamed() { - final String name = "identifier"; - mBlacklist.savePasswordBlacklist(name, Arrays.asList("one", "two", "three")); - assertEquals(mBlacklist.getName(), name); - } - - @Test - public void reportsTheCorrectNumberOfEntries() { - mBlacklist.savePasswordBlacklist("Count Entries", Arrays.asList("1", "2", "3", "4")); - assertEquals(mBlacklist.getSize(), 4); - } - - @Test - public void reportsBlacklistFile() { - assertEquals(mBlacklistFile, mBlacklist.getFile()); - } -} diff --git a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java index a98e29137fb6..f4ec867333ad 100644 --- a/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/locksettings/recoverablekeystore/RecoverableKeyStoreManagerTest.java @@ -211,24 +211,6 @@ public class RecoverableKeyStoreManagerTest { } @Test - public void generateAndStoreKey_storesTheKey() throws Exception { - int uid = Binder.getCallingUid(); - int userId = UserHandle.getCallingUserId(); - - mRecoverableKeyStoreManager.generateAndStoreKey(TEST_ALIAS); - - assertThat(mRecoverableKeyStoreDb.getKey(uid, TEST_ALIAS)).isNotNull(); - - assertThat(mRecoverableKeyStoreDb.getShouldCreateSnapshot(userId, uid)).isTrue(); - } - - @Test - public void generateAndStoreKey_returnsAKeyOfAppropriateSize() throws Exception { - assertThat(mRecoverableKeyStoreManager.generateAndStoreKey(TEST_ALIAS)) - .hasLength(RECOVERABLE_KEY_SIZE_BYTES); - } - - @Test public void importKey_storesTheKey() throws Exception { int uid = Binder.getCallingUid(); int userId = UserHandle.getCallingUserId(); @@ -265,7 +247,7 @@ public class RecoverableKeyStoreManagerTest { @Test public void removeKey_removesAKey() throws Exception { int uid = Binder.getCallingUid(); - mRecoverableKeyStoreManager.generateAndStoreKey(TEST_ALIAS); + mRecoverableKeyStoreManager.generateKey(TEST_ALIAS); mRecoverableKeyStoreManager.removeKey(TEST_ALIAS); @@ -276,7 +258,7 @@ public class RecoverableKeyStoreManagerTest { public void removeKey_updatesShouldCreateSnapshot() throws Exception { int uid = Binder.getCallingUid(); int userId = UserHandle.getCallingUserId(); - mRecoverableKeyStoreManager.generateAndStoreKey(TEST_ALIAS); + mRecoverableKeyStoreManager.generateKey(TEST_ALIAS); // Pretend that key was synced mRecoverableKeyStoreDb.setShouldCreateSnapshot(userId, uid, false); @@ -1056,7 +1038,7 @@ public class RecoverableKeyStoreManagerTest { int userId = UserHandle.getCallingUserId(); mRecoverableKeyStoreManager.setRecoverySecretTypes(new int[] { 1 }); - mRecoverableKeyStoreManager.generateAndStoreKey(TEST_ALIAS); + mRecoverableKeyStoreManager.generateKey(TEST_ALIAS); // Pretend that key was synced mRecoverableKeyStoreDb.setShouldCreateSnapshot(userId, uid, false); mRecoverableKeyStoreManager.setRecoverySecretTypes(new int[] { 2 }); diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ScheduleCalendarTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ScheduleCalendarTest.java index 36136a8932c9..ce7445788489 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/ScheduleCalendarTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/ScheduleCalendarTest.java @@ -206,6 +206,31 @@ public class ScheduleCalendarTest extends UiServiceTestCase { } @Test + public void testShouldExitForAlarm_oldAlarm() { + // Cal: today 2:15pm + Calendar cal = new GregorianCalendar(); + cal.set(Calendar.HOUR_OF_DAY, 14); + cal.set(Calendar.MINUTE, 15); + cal.set(Calendar.SECOND, 0); + cal.set(Calendar.MILLISECOND, 0); + + // ScheduleInfo: today 12:16pm - today 3:15pm + mScheduleInfo.days = new int[] {getTodayDay()}; + mScheduleInfo.startHour = 12; + mScheduleInfo.endHour = 3; + mScheduleInfo.startMinute = 16; + mScheduleInfo.endMinute = 15; + mScheduleInfo.exitAtAlarm = true; + mScheduleInfo.nextAlarm = 1000; // very old alarm + + mScheduleCalendar.setSchedule(mScheduleInfo); + assertTrue(mScheduleCalendar.isInSchedule(cal.getTimeInMillis())); + + // don't exit for an alarm if it's an old alarm + assertFalse(mScheduleCalendar.shouldExitForAlarm(1000)); + } + + @Test public void testMaybeSetNextAlarm_settingOff() { mScheduleInfo.exitAtAlarm = false; mScheduleInfo.nextAlarm = 0; diff --git a/services/tests/uiservicestests/src/com/android/server/slice/SliceManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/slice/SliceManagerServiceTest.java index 1073a800527a..5e2a36486972 100644 --- a/services/tests/uiservicestests/src/com/android/server/slice/SliceManagerServiceTest.java +++ b/services/tests/uiservicestests/src/com/android/server/slice/SliceManagerServiceTest.java @@ -15,8 +15,10 @@ package com.android.server.slice; import static android.content.ContentProvider.maybeAddUserId; +import static android.content.pm.PackageManager.PERMISSION_DENIED; import static android.content.pm.PackageManager.PERMISSION_GRANTED; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; @@ -35,9 +37,11 @@ import android.content.pm.PackageManagerInternal; import android.net.Uri; import android.os.Binder; import android.os.IBinder; +import android.os.Process; import android.os.RemoteException; import android.support.test.filters.SmallTest; import android.testing.AndroidTestingRunner; +import android.testing.TestableContext; import android.testing.TestableLooper; import android.testing.TestableLooper.RunWithLooper; @@ -63,6 +67,7 @@ public class SliceManagerServiceTest extends UiServiceTestCase { private SliceManagerService mService; private PinnedSliceState mCreatedSliceState; private IBinder mToken = new Binder(); + private TestableContext mContextSpy; @Before public void setup() { @@ -72,7 +77,8 @@ public class SliceManagerServiceTest extends UiServiceTestCase { mContext.addMockSystemService(AppOpsManager.class, mock(AppOpsManager.class)); mContext.getTestablePermissions().setPermission(TEST_URI, PERMISSION_GRANTED); - mService = spy(new SliceManagerService(mContext, TestableLooper.get(this).getLooper())); + mContextSpy = spy(mContext); + mService = spy(new SliceManagerService(mContextSpy, TestableLooper.get(this).getLooper())); mCreatedSliceState = mock(PinnedSliceState.class); doReturn(mCreatedSliceState).when(mService).createPinnedSlice(eq(TEST_URI), anyString()); } @@ -103,4 +109,23 @@ public class SliceManagerServiceTest extends UiServiceTestCase { verify(mCreatedSliceState, never()).destroy(); } + @Test + public void testCheckAutoGrantPermissions() throws RemoteException { + String[] testPerms = new String[] { + "perm1", + "perm2", + }; + when(mContextSpy.checkUriPermission(any(), anyInt(), anyInt(), anyInt())) + .thenReturn(PERMISSION_DENIED); + when(mContextSpy.checkPermission("perm1", Process.myPid(), Process.myUid())) + .thenReturn(PERMISSION_DENIED); + when(mContextSpy.checkPermission("perm2", Process.myPid(), Process.myUid())) + .thenReturn(PERMISSION_GRANTED); + mService.checkSlicePermission(TEST_URI, mContext.getPackageName(), Process.myPid(), + Process.myUid(), testPerms); + + verify(mContextSpy).checkPermission(eq("perm1"), eq(Process.myPid()), eq(Process.myUid())); + verify(mContextSpy).checkPermission(eq("perm2"), eq(Process.myPid()), eq(Process.myUid())); + } + }
\ No newline at end of file diff --git a/tests/net/java/android/app/usage/NetworkStatsManagerTest.java b/tests/net/java/android/app/usage/NetworkStatsManagerTest.java new file mode 100644 index 000000000000..25e147416d2c --- /dev/null +++ b/tests/net/java/android/app/usage/NetworkStatsManagerTest.java @@ -0,0 +1,213 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.app.usage; + +import static junit.framework.Assert.assertEquals; +import static junit.framework.Assert.assertFalse; +import static junit.framework.Assert.assertTrue; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.argThat; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.atLeastOnce; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.net.ConnectivityManager; +import android.net.INetworkStatsService; +import android.net.INetworkStatsSession; +import android.net.NetworkStats.Entry; +import android.net.NetworkStatsHistory; +import android.net.NetworkTemplate; +import android.os.RemoteException; +import android.support.test.InstrumentationRegistry; +import android.support.test.filters.SmallTest; +import android.support.test.runner.AndroidJUnit4; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.mockito.invocation.InvocationOnMock; + +@RunWith(AndroidJUnit4.class) +@SmallTest +public class NetworkStatsManagerTest { + + private @Mock INetworkStatsService mService; + private @Mock INetworkStatsSession mStatsSession; + + private NetworkStatsManager mManager; + + // TODO: change to NetworkTemplate.MATCH_MOBILE once internal constant rename is merged to aosp. + private static final int MATCH_MOBILE_ALL = 1; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + mManager = new NetworkStatsManager(InstrumentationRegistry.getContext(), mService); + } + + @Test + public void testQueryDetails() throws RemoteException { + final String subscriberId = "subid"; + final long startTime = 1; + final long endTime = 100; + final int uid1 = 10001; + final int uid2 = 10002; + final int uid3 = 10003; + + Entry uid1Entry1 = new Entry("if1", uid1, + android.net.NetworkStats.SET_DEFAULT, android.net.NetworkStats.TAG_NONE, + 100, 10, 200, 20, 0); + + Entry uid1Entry2 = new Entry( + "if2", uid1, + android.net.NetworkStats.SET_DEFAULT, android.net.NetworkStats.TAG_NONE, + 100, 10, 200, 20, 0); + + Entry uid2Entry1 = new Entry("if1", uid2, + android.net.NetworkStats.SET_DEFAULT, android.net.NetworkStats.TAG_NONE, + 150, 10, 250, 20, 0); + + Entry uid2Entry2 = new Entry( + "if2", uid2, + android.net.NetworkStats.SET_DEFAULT, android.net.NetworkStats.TAG_NONE, + 150, 10, 250, 20, 0); + + NetworkStatsHistory history1 = new NetworkStatsHistory(10, 2); + history1.recordData(10, 20, uid1Entry1); + history1.recordData(20, 30, uid1Entry2); + + NetworkStatsHistory history2 = new NetworkStatsHistory(10, 2); + history1.recordData(30, 40, uid2Entry1); + history1.recordData(35, 45, uid2Entry2); + + + when(mService.openSessionForUsageStats(anyInt(), anyString())).thenReturn(mStatsSession); + when(mStatsSession.getRelevantUids()).thenReturn(new int[] { uid1, uid2, uid3 }); + + when(mStatsSession.getHistoryIntervalForUid(any(NetworkTemplate.class), + eq(uid1), eq(android.net.NetworkStats.SET_ALL), + eq(android.net.NetworkStats.TAG_NONE), + eq(NetworkStatsHistory.FIELD_ALL), eq(startTime), eq(endTime))) + .then((InvocationOnMock inv) -> { + NetworkTemplate template = inv.getArgument(0); + assertEquals(MATCH_MOBILE_ALL, template.getMatchRule()); + assertEquals(subscriberId, template.getSubscriberId()); + return history1; + }); + + when(mStatsSession.getHistoryIntervalForUid(any(NetworkTemplate.class), + eq(uid2), eq(android.net.NetworkStats.SET_ALL), + eq(android.net.NetworkStats.TAG_NONE), + eq(NetworkStatsHistory.FIELD_ALL), eq(startTime), eq(endTime))) + .then((InvocationOnMock inv) -> { + NetworkTemplate template = inv.getArgument(0); + assertEquals(MATCH_MOBILE_ALL, template.getMatchRule()); + assertEquals(subscriberId, template.getSubscriberId()); + return history2; + }); + + + NetworkStats stats = mManager.queryDetails( + ConnectivityManager.TYPE_MOBILE, subscriberId, startTime, endTime); + + NetworkStats.Bucket bucket = new NetworkStats.Bucket(); + + // First 2 buckets exactly match entry timings + assertTrue(stats.getNextBucket(bucket)); + assertEquals(10, bucket.getStartTimeStamp()); + assertEquals(20, bucket.getEndTimeStamp()); + assertBucketMatches(uid1Entry1, bucket); + + assertTrue(stats.getNextBucket(bucket)); + assertEquals(20, bucket.getStartTimeStamp()); + assertEquals(30, bucket.getEndTimeStamp()); + assertBucketMatches(uid1Entry2, bucket); + + // 30 -> 40: contains uid2Entry1 and half of uid2Entry2 + assertTrue(stats.getNextBucket(bucket)); + assertEquals(30, bucket.getStartTimeStamp()); + assertEquals(40, bucket.getEndTimeStamp()); + assertEquals(225, bucket.getRxBytes()); + assertEquals(15, bucket.getRxPackets()); + assertEquals(375, bucket.getTxBytes()); + assertEquals(30, bucket.getTxPackets()); + + // 40 -> 50: contains half of uid2Entry2 + assertTrue(stats.getNextBucket(bucket)); + assertEquals(40, bucket.getStartTimeStamp()); + assertEquals(50, bucket.getEndTimeStamp()); + assertEquals(75, bucket.getRxBytes()); + assertEquals(5, bucket.getRxPackets()); + assertEquals(125, bucket.getTxBytes()); + assertEquals(10, bucket.getTxPackets()); + + assertFalse(stats.hasNextBucket()); + } + + @Test + public void testQueryDetails_NoSubscriberId() throws RemoteException { + final long startTime = 1; + final long endTime = 100; + final int uid1 = 10001; + final int uid2 = 10002; + + when(mService.openSessionForUsageStats(anyInt(), anyString())).thenReturn(mStatsSession); + when(mStatsSession.getRelevantUids()).thenReturn(new int[] { uid1, uid2 }); + + NetworkStats stats = mManager.queryDetails( + ConnectivityManager.TYPE_MOBILE, null, startTime, endTime); + + when(mStatsSession.getHistoryIntervalForUid(any(NetworkTemplate.class), + anyInt(), anyInt(), anyInt(), anyInt(), anyLong(), anyLong())) + .thenReturn(new NetworkStatsHistory(10, 0)); + + verify(mStatsSession, times(1)).getHistoryIntervalForUid( + argThat((NetworkTemplate t) -> + // No subscriberId: MATCH_MOBILE_WILDCARD template + t.getMatchRule() == NetworkTemplate.MATCH_MOBILE_WILDCARD), + eq(uid1), eq(android.net.NetworkStats.SET_ALL), + eq(android.net.NetworkStats.TAG_NONE), + eq(NetworkStatsHistory.FIELD_ALL), eq(startTime), eq(endTime)); + + verify(mStatsSession, times(1)).getHistoryIntervalForUid( + argThat((NetworkTemplate t) -> + // No subscriberId: MATCH_MOBILE_WILDCARD template + t.getMatchRule() == NetworkTemplate.MATCH_MOBILE_WILDCARD), + eq(uid2), eq(android.net.NetworkStats.SET_ALL), + eq(android.net.NetworkStats.TAG_NONE), + eq(NetworkStatsHistory.FIELD_ALL), eq(startTime), eq(endTime)); + + assertFalse(stats.hasNextBucket()); + } + + private void assertBucketMatches(Entry expected, + NetworkStats.Bucket actual) { + assertEquals(expected.uid, actual.getUid()); + assertEquals(expected.rxBytes, actual.getRxBytes()); + assertEquals(expected.rxPackets, actual.getRxPackets()); + assertEquals(expected.txBytes, actual.getTxBytes()); + assertEquals(expected.txPackets, actual.getTxPackets()); + } +} diff --git a/tests/net/java/android/net/util/InterfaceSetTest.java b/tests/net/java/android/net/util/InterfaceSetTest.java new file mode 100644 index 000000000000..8012838d16fc --- /dev/null +++ b/tests/net/java/android/net/util/InterfaceSetTest.java @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.net.util; + +import static junit.framework.Assert.assertEquals; +import static junit.framework.Assert.assertFalse; +import static junit.framework.Assert.assertTrue; + +import android.support.test.filters.SmallTest; +import android.support.test.runner.AndroidJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; + +@RunWith(AndroidJUnit4.class) +@SmallTest +public class InterfaceSetTest { + @Test + public void testNullNamesIgnored() { + final InterfaceSet set = new InterfaceSet(null, "if1", null, "if2", null); + assertEquals(2, set.ifnames.size()); + assertTrue(set.ifnames.contains("if1")); + assertTrue(set.ifnames.contains("if2")); + } + + @Test + public void testToString() { + final InterfaceSet set = new InterfaceSet("if1", "if2"); + final String setString = set.toString(); + assertTrue(setString.equals("[if1,if2]") || setString.equals("[if2,if1]")); + } + + @Test + public void testToString_Empty() { + final InterfaceSet set = new InterfaceSet(null, null); + assertEquals("[]", set.toString()); + } + + @Test + public void testEquals() { + assertEquals(new InterfaceSet(null, "if1", "if2"), new InterfaceSet("if2", "if1")); + assertEquals(new InterfaceSet(null, null), new InterfaceSet()); + assertFalse(new InterfaceSet("if1", "if3").equals(new InterfaceSet("if1", "if2"))); + assertFalse(new InterfaceSet("if1", "if2").equals(new InterfaceSet("if1"))); + assertFalse(new InterfaceSet().equals(null)); + } +} diff --git a/tests/net/java/com/android/server/connectivity/TetheringTest.java b/tests/net/java/com/android/server/connectivity/TetheringTest.java index e692652c7ea4..27f90f261ce0 100644 --- a/tests/net/java/com/android/server/connectivity/TetheringTest.java +++ b/tests/net/java/com/android/server/connectivity/TetheringTest.java @@ -27,8 +27,11 @@ import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_INTERFACE_NAME; import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_MODE; import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_STATE; import static android.net.wifi.WifiManager.WIFI_AP_STATE_ENABLED; + import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.argThat; +import static org.mockito.ArgumentMatchers.notNull; import static org.mockito.Matchers.anyBoolean; import static org.mockito.Matchers.anyInt; import static org.mockito.Matchers.anyString; @@ -36,6 +39,7 @@ import static org.mockito.Matchers.eq; import static org.mockito.Mockito.any; import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.never; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoMoreInteractions; @@ -45,18 +49,29 @@ import static org.mockito.Mockito.mock; import android.content.BroadcastReceiver; import android.content.ContentResolver; import android.content.Context; -import android.content.ContextWrapper; import android.content.Intent; import android.content.IntentFilter; import android.content.pm.ApplicationInfo; import android.content.res.Resources; import android.hardware.usb.UsbManager; import android.net.ConnectivityManager; -import android.net.ConnectivityManager.NetworkCallback; +import android.net.INetd; import android.net.INetworkPolicyManager; import android.net.INetworkStatsService; import android.net.InterfaceConfiguration; -import android.net.NetworkRequest; +import android.net.IpPrefix; +import android.net.LinkAddress; +import android.net.LinkProperties; +import android.net.MacAddress; +import android.net.Network; +import android.net.NetworkCapabilities; +import android.net.NetworkInfo; +import android.net.NetworkState; +import android.net.NetworkUtils; +import android.net.RouteInfo; +import android.net.ip.RouterAdvertisementDaemon; +import android.net.util.InterfaceParams; +import android.net.util.NetworkConstants; import android.net.util.SharedLog; import android.net.wifi.WifiConfiguration; import android.net.wifi.WifiManager; @@ -74,10 +89,16 @@ import android.support.test.runner.AndroidJUnit4; import android.telephony.CarrierConfigManager; import android.test.mock.MockContentResolver; +import com.android.internal.util.ArrayUtils; +import com.android.internal.util.StateMachine; import com.android.internal.util.test.BroadcastInterceptingContext; import com.android.internal.util.test.FakeSettingsProvider; +import com.android.server.connectivity.tethering.IControlsTethering; +import com.android.server.connectivity.tethering.IPv6TetheringCoordinator; import com.android.server.connectivity.tethering.OffloadHardwareInterface; +import com.android.server.connectivity.tethering.TetherInterfaceStateMachine; import com.android.server.connectivity.tethering.TetheringDependencies; +import com.android.server.connectivity.tethering.UpstreamNetworkMonitor; import org.junit.After; import org.junit.Before; @@ -86,13 +107,21 @@ import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import java.net.Inet4Address; +import java.net.Inet6Address; import java.util.ArrayList; import java.util.Vector; @RunWith(AndroidJUnit4.class) @SmallTest public class TetheringTest { + private static final int IFINDEX_OFFSET = 100; + private static final String[] PROVISIONING_APP_NAME = {"some", "app"}; + private static final String TEST_MOBILE_IFNAME = "test_rmnet_data0"; + private static final String TEST_XLAT_MOBILE_IFNAME = "v4-test_rmnet_data0"; + private static final String TEST_USB_IFNAME = "test_rndis0"; + private static final String TEST_WLAN_IFNAME = "test_wlan0"; @Mock private ApplicationInfo mApplicationInfo; @Mock private Context mContext; @@ -103,16 +132,21 @@ public class TetheringTest { @Mock private MockableSystemProperties mSystemProperties; @Mock private OffloadHardwareInterface mOffloadHardwareInterface; @Mock private Resources mResources; - @Mock private TetheringDependencies mTetheringDependencies; @Mock private UsbManager mUsbManager; @Mock private WifiManager mWifiManager; @Mock private CarrierConfigManager mCarrierConfigManager; + @Mock private UpstreamNetworkMonitor mUpstreamNetworkMonitor; + @Mock private IPv6TetheringCoordinator mIPv6TetheringCoordinator; + @Mock private RouterAdvertisementDaemon mRouterAdvertisementDaemon; + @Mock private INetd mNetd; + + private final MockTetheringDependencies mTetheringDependencies = + new MockTetheringDependencies(); // Like so many Android system APIs, these cannot be mocked because it is marked final. // We have to use the real versions. private final PersistableBundle mCarrierConfig = new PersistableBundle(); private final TestLooper mLooper = new TestLooper(); - private final String mTestIfname = "test_wlan0"; private Vector<Intent> mIntents; private BroadcastInterceptingContext mServiceContext; @@ -146,23 +180,122 @@ public class TetheringTest { } } + public class MockTetheringDependencies extends TetheringDependencies { + private StateMachine upstreamNetworkMonitorMasterSM; + private ArrayList<TetherInterfaceStateMachine> ipv6CoordinatorNotifyList; + + @Override + public OffloadHardwareInterface getOffloadHardwareInterface(Handler h, SharedLog log) { + return mOffloadHardwareInterface; + } + + @Override + public UpstreamNetworkMonitor getUpstreamNetworkMonitor(Context ctx, + StateMachine target, SharedLog log, int what) { + upstreamNetworkMonitorMasterSM = target; + return mUpstreamNetworkMonitor; + } + + @Override + public IPv6TetheringCoordinator getIPv6TetheringCoordinator( + ArrayList<TetherInterfaceStateMachine> notifyList, SharedLog log) { + ipv6CoordinatorNotifyList = notifyList; + return mIPv6TetheringCoordinator; + } + + @Override + public RouterAdvertisementDaemon getRouterAdvertisementDaemon(InterfaceParams ifParams) { + return mRouterAdvertisementDaemon; + } + + @Override + public INetd getNetdService() { + return mNetd; + } + + @Override + public InterfaceParams getInterfaceParams(String ifName) { + final String[] ifaces = new String[] { TEST_USB_IFNAME, TEST_WLAN_IFNAME, + TEST_MOBILE_IFNAME }; + final int index = ArrayUtils.indexOf(ifaces, ifName); + assertTrue("Non-mocked interface: " + ifName, index >= 0); + return new InterfaceParams(ifName, index + IFINDEX_OFFSET, + MacAddress.ALL_ZEROS_ADDRESS); + } + } + + private static NetworkState buildMobileUpstreamState(boolean withIPv4, boolean withIPv6, + boolean with464xlat) { + final NetworkInfo info = new NetworkInfo(ConnectivityManager.TYPE_MOBILE, 0, null, null); + info.setDetailedState(NetworkInfo.DetailedState.CONNECTED, null, null); + final LinkProperties prop = new LinkProperties(); + prop.setInterfaceName(TEST_MOBILE_IFNAME); + + if (withIPv4) { + prop.addRoute(new RouteInfo(new IpPrefix(Inet4Address.ANY, 0), + NetworkUtils.numericToInetAddress("10.0.0.1"), TEST_MOBILE_IFNAME)); + } + + if (withIPv6) { + prop.addDnsServer(NetworkUtils.numericToInetAddress("2001:db8::2")); + prop.addLinkAddress( + new LinkAddress(NetworkUtils.numericToInetAddress("2001:db8::"), + NetworkConstants.RFC7421_PREFIX_LENGTH)); + prop.addRoute(new RouteInfo(new IpPrefix(Inet6Address.ANY, 0), + NetworkUtils.numericToInetAddress("2001:db8::1"), TEST_MOBILE_IFNAME)); + } + + if (with464xlat) { + final LinkProperties stackedLink = new LinkProperties(); + stackedLink.setInterfaceName(TEST_XLAT_MOBILE_IFNAME); + stackedLink.addRoute(new RouteInfo(new IpPrefix(Inet4Address.ANY, 0), + NetworkUtils.numericToInetAddress("192.0.0.1"), TEST_XLAT_MOBILE_IFNAME)); + + prop.addStackedLink(stackedLink); + } + + + final NetworkCapabilities capabilities = new NetworkCapabilities() + .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR);; + return new NetworkState(info, prop, capabilities, new Network(100), null, "netid"); + } + + private static NetworkState buildMobileIPv4UpstreamState() { + return buildMobileUpstreamState(true, false, false); + } + + private static NetworkState buildMobileIPv6UpstreamState() { + return buildMobileUpstreamState(false, true, false); + } + + private static NetworkState buildMobileDualStackUpstreamState() { + return buildMobileUpstreamState(true, true, false); + } + + private static NetworkState buildMobile464xlatUpstreamState() { + return buildMobileUpstreamState(false, true, true); + } + @Before public void setUp() throws Exception { MockitoAnnotations.initMocks(this); when(mResources.getStringArray(com.android.internal.R.array.config_tether_dhcp_range)) .thenReturn(new String[0]); when(mResources.getStringArray(com.android.internal.R.array.config_tether_usb_regexs)) - .thenReturn(new String[0]); + .thenReturn(new String[] { "test_rndis\\d" }); when(mResources.getStringArray(com.android.internal.R.array.config_tether_wifi_regexs)) - .thenReturn(new String[]{ "test_wlan\\d", "test_rndis\\d" }); + .thenReturn(new String[]{ "test_wlan\\d" }); when(mResources.getStringArray(com.android.internal.R.array.config_tether_bluetooth_regexs)) .thenReturn(new String[0]); when(mResources.getIntArray(com.android.internal.R.array.config_tether_upstream_types)) .thenReturn(new int[0]); when(mNMService.listInterfaces()) - .thenReturn(new String[]{ "test_rmnet_data0", mTestIfname }); + .thenReturn(new String[] { + TEST_MOBILE_IFNAME, TEST_WLAN_IFNAME, TEST_USB_IFNAME}); when(mNMService.getInterfaceConfig(anyString())) .thenReturn(new InterfaceConfiguration()); + when(mRouterAdvertisementDaemon.start()) + .thenReturn(true); mServiceContext = new MockContext(mContext); mContentResolver = new MockContentResolver(mServiceContext); @@ -176,8 +309,6 @@ public class TetheringTest { }; mServiceContext.registerReceiver(mBroadcastReceiver, new IntentFilter(ConnectivityManager.ACTION_TETHER_STATE_CHANGED)); - when(mTetheringDependencies.getOffloadHardwareInterface( - any(Handler.class), any(SharedLog.class))).thenReturn(mOffloadHardwareInterface); mTethering = new Tethering(mServiceContext, mNMService, mStatsService, mPolicyManager, mLooper.getLooper(), mSystemProperties, mTetheringDependencies); @@ -264,10 +395,10 @@ public class TetheringTest { } private void verifyInterfaceServingModeStarted() throws Exception { - verify(mNMService, times(1)).getInterfaceConfig(mTestIfname); + verify(mNMService, times(1)).getInterfaceConfig(TEST_WLAN_IFNAME); verify(mNMService, times(1)) - .setInterfaceConfig(eq(mTestIfname), any(InterfaceConfiguration.class)); - verify(mNMService, times(1)).tetherInterface(mTestIfname); + .setInterfaceConfig(eq(TEST_WLAN_IFNAME), any(InterfaceConfiguration.class)); + verify(mNMService, times(1)).tetherInterface(TEST_WLAN_IFNAME); } private void verifyTetheringBroadcast(String ifname, String whichExtra) { @@ -287,7 +418,7 @@ public class TetheringTest { // per-interface state machine to start up, and telling us that // hotspot mode is to be started. if (emulateInterfaceStatusChanged) { - mTethering.interfaceStatusChanged(mTestIfname, true); + mTethering.interfaceStatusChanged(TEST_WLAN_IFNAME, true); } sendWifiApStateChanged(WIFI_AP_STATE_ENABLED); mLooper.dispatchAll(); @@ -297,27 +428,31 @@ public class TetheringTest { // broadcast indicating that the interface is "available". if (emulateInterfaceStatusChanged) { verify(mConnectivityManager, atLeastOnce()).isTetheringSupported(); - verifyTetheringBroadcast(mTestIfname, ConnectivityManager.EXTRA_AVAILABLE_TETHER); + verifyTetheringBroadcast(TEST_WLAN_IFNAME, ConnectivityManager.EXTRA_AVAILABLE_TETHER); } verifyNoMoreInteractions(mConnectivityManager); verifyNoMoreInteractions(mNMService); verifyNoMoreInteractions(mWifiManager); } - @Test - public void testUsbConfiguredBroadcastStartsTethering() throws Exception { + private void prepareUsbTethering(NetworkState upstreamState) { when(mConnectivityManager.isTetheringSupported()).thenReturn(true); + when(mUpstreamNetworkMonitor.selectPreferredUpstreamType(any())) + .thenReturn(upstreamState); // Emulate pressing the USB tethering button in Settings UI. mTethering.startTethering(TETHERING_USB, null, false); mLooper.dispatchAll(); verify(mUsbManager, times(1)).setCurrentFunctions(UsbManager.FUNCTION_RNDIS); - // Pretend we receive a USB connected broadcast. Here we also pretend - // that the RNDIS function is somehow enabled, so that we see if we - // might trip ourselves up. - sendUsbBroadcast(true, false, true); - mLooper.dispatchAll(); + mTethering.interfaceStatusChanged(TEST_USB_IFNAME, true); + } + + @Test + public void testUsbConfiguredBroadcastStartsTethering() throws Exception { + NetworkState upstreamState = buildMobileIPv4UpstreamState(); + prepareUsbTethering(upstreamState); + // This should produce no activity of any kind. verifyNoMoreInteractions(mConnectivityManager); verifyNoMoreInteractions(mNMService); @@ -328,6 +463,10 @@ public class TetheringTest { // Now we should see the start of tethering mechanics (in this case: // tetherMatchingInterfaces() which starts by fetching all interfaces). verify(mNMService, times(1)).listInterfaces(); + + // UpstreamNetworkMonitor should receive selected upstream + verify(mUpstreamNetworkMonitor, times(1)).selectPreferredUpstreamType(any()); + verify(mUpstreamNetworkMonitor, times(1)).setCurrentUpstream(upstreamState.network); } @Test @@ -348,26 +487,21 @@ public class TetheringTest { // per-interface state machine to start up, and telling us that // hotspot mode is to be started. if (emulateInterfaceStatusChanged) { - mTethering.interfaceStatusChanged(mTestIfname, true); + mTethering.interfaceStatusChanged(TEST_WLAN_IFNAME, true); } - sendWifiApStateChanged(WIFI_AP_STATE_ENABLED, mTestIfname, IFACE_IP_MODE_LOCAL_ONLY); + sendWifiApStateChanged(WIFI_AP_STATE_ENABLED, TEST_WLAN_IFNAME, IFACE_IP_MODE_LOCAL_ONLY); mLooper.dispatchAll(); verifyInterfaceServingModeStarted(); - verifyTetheringBroadcast(mTestIfname, ConnectivityManager.EXTRA_AVAILABLE_TETHER); + verifyTetheringBroadcast(TEST_WLAN_IFNAME, ConnectivityManager.EXTRA_AVAILABLE_TETHER); verify(mNMService, times(1)).setIpForwardingEnabled(true); verify(mNMService, times(1)).startTethering(any(String[].class)); verifyNoMoreInteractions(mNMService); verify(mWifiManager).updateInterfaceIpState( - mTestIfname, WifiManager.IFACE_IP_MODE_LOCAL_ONLY); + TEST_WLAN_IFNAME, WifiManager.IFACE_IP_MODE_LOCAL_ONLY); verifyNoMoreInteractions(mWifiManager); - verifyTetheringBroadcast(mTestIfname, ConnectivityManager.EXTRA_ACTIVE_LOCAL_ONLY); - // UpstreamNetworkMonitor will be started, and will register two callbacks: - // a "listen all" and a "track default". - verify(mConnectivityManager, times(1)).registerNetworkCallback( - any(NetworkRequest.class), any(NetworkCallback.class), any(Handler.class)); - verify(mConnectivityManager, times(1)).registerDefaultNetworkCallback( - any(NetworkCallback.class), any(Handler.class)); + verifyTetheringBroadcast(TEST_WLAN_IFNAME, ConnectivityManager.EXTRA_ACTIVE_LOCAL_ONLY); + verify(mUpstreamNetworkMonitor, times(1)).start(); // TODO: Figure out why this isn't exactly once, for sendTetherStateChangedBroadcast(). verify(mConnectivityManager, atLeastOnce()).isTetheringSupported(); verifyNoMoreInteractions(mConnectivityManager); @@ -375,14 +509,14 @@ public class TetheringTest { // Emulate externally-visible WifiManager effects, when hotspot mode // is being torn down. sendWifiApStateChanged(WifiManager.WIFI_AP_STATE_DISABLED); - mTethering.interfaceRemoved(mTestIfname); + mTethering.interfaceRemoved(TEST_WLAN_IFNAME); mLooper.dispatchAll(); - verify(mNMService, times(1)).untetherInterface(mTestIfname); + verify(mNMService, times(1)).untetherInterface(TEST_WLAN_IFNAME); // TODO: Why is {g,s}etInterfaceConfig() called more than once? - verify(mNMService, atLeastOnce()).getInterfaceConfig(mTestIfname); + verify(mNMService, atLeastOnce()).getInterfaceConfig(TEST_WLAN_IFNAME); verify(mNMService, atLeastOnce()) - .setInterfaceConfig(eq(mTestIfname), any(InterfaceConfiguration.class)); + .setInterfaceConfig(eq(TEST_WLAN_IFNAME), any(InterfaceConfiguration.class)); verify(mNMService, times(1)).stopTethering(); verify(mNMService, times(1)).setIpForwardingEnabled(false); verifyNoMoreInteractions(mNMService); @@ -390,7 +524,115 @@ public class TetheringTest { // Asking for the last error after the per-interface state machine // has been reaped yields an unknown interface error. assertEquals(ConnectivityManager.TETHER_ERROR_UNKNOWN_IFACE, - mTethering.getLastTetherError(mTestIfname)); + mTethering.getLastTetherError(TEST_WLAN_IFNAME)); + } + + /** + * Send CMD_IPV6_TETHER_UPDATE to TISMs as would be done by IPv6TetheringCoordinator. + */ + private void sendIPv6TetherUpdates(NetworkState upstreamState) { + // IPv6TetheringCoordinator must have been notified of downstream + verify(mIPv6TetheringCoordinator, times(1)).addActiveDownstream( + argThat(sm -> sm.linkProperties().getInterfaceName().equals(TEST_USB_IFNAME)), + eq(IControlsTethering.STATE_TETHERED)); + + for (TetherInterfaceStateMachine tism : + mTetheringDependencies.ipv6CoordinatorNotifyList) { + NetworkState ipv6OnlyState = buildMobileUpstreamState(false, true, false); + tism.sendMessage(TetherInterfaceStateMachine.CMD_IPV6_TETHER_UPDATE, 0, 0, + upstreamState.linkProperties.isIPv6Provisioned() + ? ipv6OnlyState.linkProperties + : null); + } + mLooper.dispatchAll(); + } + + private void runUsbTethering(NetworkState upstreamState) { + prepareUsbTethering(upstreamState); + sendUsbBroadcast(true, true, true); + mLooper.dispatchAll(); + } + + @Test + public void workingMobileUsbTethering_IPv4() throws Exception { + NetworkState upstreamState = buildMobileIPv4UpstreamState(); + runUsbTethering(upstreamState); + + verify(mNMService, times(1)).enableNat(TEST_USB_IFNAME, TEST_MOBILE_IFNAME); + verify(mNMService, times(1)).startInterfaceForwarding(TEST_USB_IFNAME, TEST_MOBILE_IFNAME); + + sendIPv6TetherUpdates(upstreamState); + verify(mRouterAdvertisementDaemon, never()).buildNewRa(any(), notNull()); + } + + @Test + public void workingMobileUsbTethering_IPv6() throws Exception { + NetworkState upstreamState = buildMobileIPv6UpstreamState(); + runUsbTethering(upstreamState); + + verify(mNMService, times(1)).enableNat(TEST_USB_IFNAME, TEST_MOBILE_IFNAME); + verify(mNMService, times(1)).startInterfaceForwarding(TEST_USB_IFNAME, TEST_MOBILE_IFNAME); + + sendIPv6TetherUpdates(upstreamState); + verify(mRouterAdvertisementDaemon, times(1)).buildNewRa(any(), notNull()); + verify(mNetd, times(1)).tetherApplyDnsInterfaces(); + } + + @Test + public void workingMobileUsbTethering_DualStack() throws Exception { + NetworkState upstreamState = buildMobileDualStackUpstreamState(); + runUsbTethering(upstreamState); + + verify(mNMService, times(1)).enableNat(TEST_USB_IFNAME, TEST_MOBILE_IFNAME); + verify(mNMService, times(1)).startInterfaceForwarding(TEST_USB_IFNAME, TEST_MOBILE_IFNAME); + verify(mRouterAdvertisementDaemon, times(1)).start(); + + sendIPv6TetherUpdates(upstreamState); + verify(mRouterAdvertisementDaemon, times(1)).buildNewRa(any(), notNull()); + verify(mNetd, times(1)).tetherApplyDnsInterfaces(); + } + + @Test + public void workingMobileUsbTethering_MultipleUpstreams() throws Exception { + NetworkState upstreamState = buildMobile464xlatUpstreamState(); + runUsbTethering(upstreamState); + + verify(mNMService, times(1)).enableNat(TEST_USB_IFNAME, TEST_XLAT_MOBILE_IFNAME); + verify(mNMService, times(1)).enableNat(TEST_USB_IFNAME, TEST_MOBILE_IFNAME); + verify(mNMService, times(1)).startInterfaceForwarding(TEST_USB_IFNAME, TEST_MOBILE_IFNAME); + verify(mNMService, times(1)).startInterfaceForwarding(TEST_USB_IFNAME, + TEST_XLAT_MOBILE_IFNAME); + + sendIPv6TetherUpdates(upstreamState); + verify(mRouterAdvertisementDaemon, times(1)).buildNewRa(any(), notNull()); + verify(mNetd, times(1)).tetherApplyDnsInterfaces(); + } + + @Test + public void workingMobileUsbTethering_v6Then464xlat() throws Exception { + // Setup IPv6 + NetworkState upstreamState = buildMobileIPv6UpstreamState(); + runUsbTethering(upstreamState); + + // Then 464xlat comes up + upstreamState = buildMobile464xlatUpstreamState(); + when(mUpstreamNetworkMonitor.selectPreferredUpstreamType(any())) + .thenReturn(upstreamState); + + // Upstream LinkProperties changed: UpstreamNetworkMonitor sends EVENT_ON_LINKPROPERTIES. + mTetheringDependencies.upstreamNetworkMonitorMasterSM.sendMessage( + Tethering.TetherMasterSM.EVENT_UPSTREAM_CALLBACK, + UpstreamNetworkMonitor.EVENT_ON_LINKPROPERTIES, + 0, + upstreamState); + mLooper.dispatchAll(); + + // Forwarding is added for 464xlat, and was still added only once for v6 + verify(mNMService, times(1)).enableNat(TEST_USB_IFNAME, TEST_XLAT_MOBILE_IFNAME); + verify(mNMService, times(1)).enableNat(TEST_USB_IFNAME, TEST_MOBILE_IFNAME); + verify(mNMService, times(1)).startInterfaceForwarding(TEST_USB_IFNAME, TEST_MOBILE_IFNAME); + verify(mNMService, times(1)).startInterfaceForwarding(TEST_USB_IFNAME, + TEST_XLAT_MOBILE_IFNAME); } @Test @@ -420,12 +662,12 @@ public class TetheringTest { // Emulate externally-visible WifiManager effects, causing the // per-interface state machine to start up, and telling us that // tethering mode is to be started. - mTethering.interfaceStatusChanged(mTestIfname, true); + mTethering.interfaceStatusChanged(TEST_WLAN_IFNAME, true); sendWifiApStateChanged(WIFI_AP_STATE_ENABLED); mLooper.dispatchAll(); verify(mConnectivityManager, atLeastOnce()).isTetheringSupported(); - verifyTetheringBroadcast(mTestIfname, ConnectivityManager.EXTRA_AVAILABLE_TETHER); + verifyTetheringBroadcast(TEST_WLAN_IFNAME, ConnectivityManager.EXTRA_AVAILABLE_TETHER); verifyNoMoreInteractions(mConnectivityManager); verifyNoMoreInteractions(mNMService); verifyNoMoreInteractions(mWifiManager); @@ -448,30 +690,23 @@ public class TetheringTest { // Emulate externally-visible WifiManager effects, causing the // per-interface state machine to start up, and telling us that // tethering mode is to be started. - mTethering.interfaceStatusChanged(mTestIfname, true); - sendWifiApStateChanged(WIFI_AP_STATE_ENABLED, mTestIfname, IFACE_IP_MODE_TETHERED); + mTethering.interfaceStatusChanged(TEST_WLAN_IFNAME, true); + sendWifiApStateChanged(WIFI_AP_STATE_ENABLED, TEST_WLAN_IFNAME, IFACE_IP_MODE_TETHERED); mLooper.dispatchAll(); verifyInterfaceServingModeStarted(); - verifyTetheringBroadcast(mTestIfname, ConnectivityManager.EXTRA_AVAILABLE_TETHER); + verifyTetheringBroadcast(TEST_WLAN_IFNAME, ConnectivityManager.EXTRA_AVAILABLE_TETHER); verify(mNMService, times(1)).setIpForwardingEnabled(true); verify(mNMService, times(1)).startTethering(any(String[].class)); verifyNoMoreInteractions(mNMService); verify(mWifiManager).updateInterfaceIpState( - mTestIfname, WifiManager.IFACE_IP_MODE_TETHERED); + TEST_WLAN_IFNAME, WifiManager.IFACE_IP_MODE_TETHERED); verifyNoMoreInteractions(mWifiManager); - verifyTetheringBroadcast(mTestIfname, ConnectivityManager.EXTRA_ACTIVE_TETHER); - // UpstreamNetworkMonitor will be started, and will register two callbacks: - // a "listen all" and a "track default". - verify(mConnectivityManager, times(1)).registerNetworkCallback( - any(NetworkRequest.class), any(NetworkCallback.class), any(Handler.class)); - verify(mConnectivityManager, times(1)).registerDefaultNetworkCallback( - any(NetworkCallback.class), any(Handler.class)); + verifyTetheringBroadcast(TEST_WLAN_IFNAME, ConnectivityManager.EXTRA_ACTIVE_TETHER); + verify(mUpstreamNetworkMonitor, times(1)).start(); // In tethering mode, in the default configuration, an explicit request // for a mobile network is also made. - verify(mConnectivityManager, times(1)).requestNetwork( - any(NetworkRequest.class), any(NetworkCallback.class), eq(0), anyInt(), - any(Handler.class)); + verify(mUpstreamNetworkMonitor, times(1)).registerMobileNetworkRequest(); // TODO: Figure out why this isn't exactly once, for sendTetherStateChangedBroadcast(). verify(mConnectivityManager, atLeastOnce()).isTetheringSupported(); verifyNoMoreInteractions(mConnectivityManager); @@ -494,14 +729,14 @@ public class TetheringTest { // Emulate externally-visible WifiManager effects, when tethering mode // is being torn down. sendWifiApStateChanged(WifiManager.WIFI_AP_STATE_DISABLED); - mTethering.interfaceRemoved(mTestIfname); + mTethering.interfaceRemoved(TEST_WLAN_IFNAME); mLooper.dispatchAll(); - verify(mNMService, times(1)).untetherInterface(mTestIfname); + verify(mNMService, times(1)).untetherInterface(TEST_WLAN_IFNAME); // TODO: Why is {g,s}etInterfaceConfig() called more than once? - verify(mNMService, atLeastOnce()).getInterfaceConfig(mTestIfname); + verify(mNMService, atLeastOnce()).getInterfaceConfig(TEST_WLAN_IFNAME); verify(mNMService, atLeastOnce()) - .setInterfaceConfig(eq(mTestIfname), any(InterfaceConfiguration.class)); + .setInterfaceConfig(eq(TEST_WLAN_IFNAME), any(InterfaceConfiguration.class)); verify(mNMService, times(1)).stopTethering(); verify(mNMService, times(1)).setIpForwardingEnabled(false); verifyNoMoreInteractions(mNMService); @@ -509,7 +744,7 @@ public class TetheringTest { // Asking for the last error after the per-interface state machine // has been reaped yields an unknown interface error. assertEquals(ConnectivityManager.TETHER_ERROR_UNKNOWN_IFACE, - mTethering.getLastTetherError(mTestIfname)); + mTethering.getLastTetherError(TEST_WLAN_IFNAME)); } // TODO: Test with and without interfaceStatusChanged(). @@ -530,21 +765,21 @@ public class TetheringTest { // Emulate externally-visible WifiManager effects, causing the // per-interface state machine to start up, and telling us that // tethering mode is to be started. - mTethering.interfaceStatusChanged(mTestIfname, true); - sendWifiApStateChanged(WIFI_AP_STATE_ENABLED, mTestIfname, IFACE_IP_MODE_TETHERED); + mTethering.interfaceStatusChanged(TEST_WLAN_IFNAME, true); + sendWifiApStateChanged(WIFI_AP_STATE_ENABLED, TEST_WLAN_IFNAME, IFACE_IP_MODE_TETHERED); mLooper.dispatchAll(); // We verify get/set called twice here: once for setup and once during // teardown because all events happen over the course of the single // dispatchAll() above. - verify(mNMService, times(2)).getInterfaceConfig(mTestIfname); + verify(mNMService, times(2)).getInterfaceConfig(TEST_WLAN_IFNAME); verify(mNMService, times(2)) - .setInterfaceConfig(eq(mTestIfname), any(InterfaceConfiguration.class)); - verify(mNMService, times(1)).tetherInterface(mTestIfname); + .setInterfaceConfig(eq(TEST_WLAN_IFNAME), any(InterfaceConfiguration.class)); + verify(mNMService, times(1)).tetherInterface(TEST_WLAN_IFNAME); verify(mWifiManager).updateInterfaceIpState( - mTestIfname, WifiManager.IFACE_IP_MODE_TETHERED); + TEST_WLAN_IFNAME, WifiManager.IFACE_IP_MODE_TETHERED); verify(mConnectivityManager, atLeastOnce()).isTetheringSupported(); - verifyTetheringBroadcast(mTestIfname, ConnectivityManager.EXTRA_AVAILABLE_TETHER); + verifyTetheringBroadcast(TEST_WLAN_IFNAME, ConnectivityManager.EXTRA_AVAILABLE_TETHER); // This is called, but will throw. verify(mNMService, times(1)).setIpForwardingEnabled(true); // This never gets called because of the exception thrown above. @@ -552,9 +787,9 @@ public class TetheringTest { // When the master state machine transitions to an error state it tells // downstream interfaces, which causes us to tell Wi-Fi about the error // so it can take down AP mode. - verify(mNMService, times(1)).untetherInterface(mTestIfname); + verify(mNMService, times(1)).untetherInterface(TEST_WLAN_IFNAME); verify(mWifiManager).updateInterfaceIpState( - mTestIfname, WifiManager.IFACE_IP_MODE_CONFIGURATION_ERROR); + TEST_WLAN_IFNAME, WifiManager.IFACE_IP_MODE_CONFIGURATION_ERROR); verifyNoMoreInteractions(mWifiManager); verifyNoMoreInteractions(mConnectivityManager); @@ -596,7 +831,7 @@ public class TetheringTest { @Test public void testDisallowTetheringWhenAtLeastOneTetheringInterfaceIsActive() throws Exception { - final String[] nonEmptyActiveIfacesList = new String[]{mTestIfname}; + final String[] nonEmptyActiveIfacesList = new String[]{TEST_WLAN_IFNAME}; final boolean currDisallow = false; final boolean nextDisallow = true; final int expectedInteractionsWithShowNotification = 1; @@ -618,7 +853,7 @@ public class TetheringTest { @Test public void testAllowTetheringWhenAtLeastOneTetheringInterfaceIsActive() throws Exception { - final String[] nonEmptyActiveIfacesList = new String[]{mTestIfname}; + final String[] nonEmptyActiveIfacesList = new String[]{TEST_WLAN_IFNAME}; final boolean currDisallow = true; final boolean nextDisallow = false; final int expectedInteractionsWithShowNotification = 0; @@ -629,7 +864,7 @@ public class TetheringTest { @Test public void testDisallowTetheringUnchanged() throws Exception { - final String[] nonEmptyActiveIfacesList = new String[]{mTestIfname}; + final String[] nonEmptyActiveIfacesList = new String[]{TEST_WLAN_IFNAME}; final int expectedInteractionsWithShowNotification = 0; boolean currDisallow = true; boolean nextDisallow = true; diff --git a/tests/net/java/com/android/server/connectivity/VpnTest.java b/tests/net/java/com/android/server/connectivity/VpnTest.java index f59850d45ae3..e377a472530e 100644 --- a/tests/net/java/com/android/server/connectivity/VpnTest.java +++ b/tests/net/java/com/android/server/connectivity/VpnTest.java @@ -70,6 +70,7 @@ import android.os.Build.VERSION_CODES; import android.os.Bundle; import android.os.INetworkManagementService; import android.os.Looper; +import android.os.SystemClock; import android.os.UserHandle; import android.os.UserManager; import android.support.test.filters.SmallTest; @@ -88,6 +89,8 @@ import org.mockito.InOrder; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import java.net.Inet4Address; +import java.net.UnknownHostException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -639,4 +642,32 @@ public class VpnTest { lp.addRoute(new RouteInfo(new IpPrefix("::/1"))); assertTrue(Vpn.providesRoutesToMostDestinations(lp)); } + + @Test + public void testDoesNotLockUpWithTooManyRoutes() { + final LinkProperties lp = new LinkProperties(); + final byte[] ad = new byte[4]; + // Actually evaluating this many routes under 1500ms is impossible on + // current hardware and for some time, as the algorithm is O(n²). + // Make sure the system has a safeguard against this and does not + // lock up. + final int MAX_ROUTES = 4000; + final long MAX_ALLOWED_TIME_MS = 1500; + for (int i = 0; i < MAX_ROUTES; ++i) { + ad[0] = (byte)((i >> 24) & 0xFF); + ad[1] = (byte)((i >> 16) & 0xFF); + ad[2] = (byte)((i >> 8) & 0xFF); + ad[3] = (byte)(i & 0xFF); + try { + lp.addRoute(new RouteInfo(new IpPrefix(Inet4Address.getByAddress(ad), 32))); + } catch (UnknownHostException e) { + // UnknownHostException is only thrown for an address of illegal length, + // which can't happen in the case above. + } + } + final long start = SystemClock.currentThreadTimeMillis(); + assertTrue(Vpn.providesRoutesToMostDestinations(lp)); + final long end = SystemClock.currentThreadTimeMillis(); + assertTrue(end - start < MAX_ALLOWED_TIME_MS); + } } diff --git a/tests/net/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachineTest.java b/tests/net/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachineTest.java index db5373ac34b2..7c77cf59524d 100644 --- a/tests/net/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachineTest.java +++ b/tests/net/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachineTest.java @@ -31,7 +31,6 @@ import static org.mockito.Mockito.when; import static android.net.ConnectivityManager.TETHER_ERROR_ENABLE_NAT_ERROR; import static android.net.ConnectivityManager.TETHER_ERROR_NO_ERROR; import static android.net.ConnectivityManager.TETHER_ERROR_TETHER_IFACE_ERROR; -import static android.net.ConnectivityManager.TETHER_ERROR_UNTETHER_IFACE_ERROR; import static android.net.ConnectivityManager.TETHERING_BLUETOOTH; import static android.net.ConnectivityManager.TETHERING_USB; import static android.net.ConnectivityManager.TETHERING_WIFI; @@ -39,12 +38,12 @@ import static com.android.server.connectivity.tethering.IControlsTethering.STATE import static com.android.server.connectivity.tethering.IControlsTethering.STATE_TETHERED; import static com.android.server.connectivity.tethering.IControlsTethering.STATE_UNAVAILABLE; -import android.net.ConnectivityManager; import android.net.INetworkStatsService; import android.net.InterfaceConfiguration; import android.net.LinkAddress; import android.net.LinkProperties; import android.net.RouteInfo; +import android.net.util.InterfaceSet; import android.net.util.SharedLog; import android.os.INetworkManagementService; import android.os.RemoteException; @@ -75,6 +74,7 @@ public class TetherInterfaceStateMachineTest { @Mock private IControlsTethering mTetherHelper; @Mock private InterfaceConfiguration mInterfaceConfiguration; @Mock private SharedLog mSharedLog; + @Mock private TetheringDependencies mTetheringDependencies; private final TestLooper mLooper = new TestLooper(); private final ArgumentCaptor<LinkProperties> mLinkPropertiesCaptor = @@ -84,7 +84,7 @@ public class TetherInterfaceStateMachineTest { private void initStateMachine(int interfaceType) throws Exception { mTestedSm = new TetherInterfaceStateMachine( IFACE_NAME, mLooper.getLooper(), interfaceType, mSharedLog, - mNMService, mStatsService, mTetherHelper); + mNMService, mStatsService, mTetherHelper, mTetheringDependencies); mTestedSm.start(); // Starting the state machine always puts us in a consistent state and notifies // the rest of the world that we've changed from an unknown to available state. @@ -111,7 +111,8 @@ public class TetherInterfaceStateMachineTest { @Test public void startsOutAvailable() { mTestedSm = new TetherInterfaceStateMachine(IFACE_NAME, mLooper.getLooper(), - TETHERING_BLUETOOTH, mSharedLog, mNMService, mStatsService, mTetherHelper); + TETHERING_BLUETOOTH, mSharedLog, mNMService, mStatsService, mTetherHelper, + mTetheringDependencies); mTestedSm.start(); mLooper.dispatchAll(); verify(mTetherHelper).updateInterfaceState( @@ -346,7 +347,7 @@ public class TetherInterfaceStateMachineTest { * Send a command to the state machine under test, and run the event loop to idle. * * @param command One of the TetherInterfaceStateMachine.CMD_* constants. - * @param obj An additional argument to pass. + * @param arg1 An additional argument to pass. */ private void dispatchCommand(int command, int arg1) { mTestedSm.sendMessage(command, arg1); @@ -371,7 +372,7 @@ public class TetherInterfaceStateMachineTest { */ private void dispatchTetherConnectionChanged(String upstreamIface) { mTestedSm.sendMessage(TetherInterfaceStateMachine.CMD_TETHER_CONNECTION_CHANGED, - upstreamIface); + new InterfaceSet(upstreamIface)); mLooper.dispatchAll(); } diff --git a/tests/net/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitorTest.java b/tests/net/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitorTest.java index c3b9defdec4e..9661dc24ca2e 100644 --- a/tests/net/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitorTest.java +++ b/tests/net/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitorTest.java @@ -147,6 +147,16 @@ public class UpstreamNetworkMonitorTest { } @Test + public void testCallbacksRegistered() { + mUNM.start(); + verify(mCM, times(1)).registerNetworkCallback(any(), any(), any()); + verify(mCM, times(1)).registerDefaultNetworkCallback(any(), any()); + + mUNM.stop(); + verify(mCM, times(2)).unregisterNetworkCallback(any(NetworkCallback.class)); + } + + @Test public void testRequestsMobileNetwork() throws Exception { assertFalse(mUNM.mobileNetworkRequested()); assertEquals(0, mCM.requested.size()); |