diff options
308 files changed, 8519 insertions, 2376 deletions
diff --git a/CleanSpec.mk b/CleanSpec.mk index 2247e43758d7..6deda0caa9aa 100644 --- a/CleanSpec.mk +++ b/CleanSpec.mk @@ -247,6 +247,7 @@ $(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/statsd $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/framework/com.android.mediadrm.signer.jar) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/framework/com.android.location.provider.jar) $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/framework/com.android.future.usb.accessory.jar) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/framework/com.android.media.remotedisplay.jar) # ****************************************************************** # NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST ABOVE THIS BANNER # ****************************************************************** diff --git a/api/current.txt b/api/current.txt index 87aaaa671da3..e6a31afafa1d 100755 --- a/api/current.txt +++ b/api/current.txt @@ -7,6 +7,7 @@ package android { public static final class Manifest.permission { ctor public Manifest.permission(); field public static final java.lang.String ACCEPT_HANDOVER = "android.permission.ACCEPT_HANDOVER"; + field public static final java.lang.String ACCESS_BACKGROUND_LOCATION = "android.permission.ACCESS_BACKGROUND_LOCATION"; field public static final java.lang.String ACCESS_CHECKIN_PROPERTIES = "android.permission.ACCESS_CHECKIN_PROPERTIES"; field public static final java.lang.String ACCESS_COARSE_LOCATION = "android.permission.ACCESS_COARSE_LOCATION"; field public static final java.lang.String ACCESS_FINE_LOCATION = "android.permission.ACCESS_FINE_LOCATION"; @@ -777,6 +778,7 @@ package android { field public static final int isModifier = 16843334; // 0x1010246 field public static final int isRepeatable = 16843336; // 0x1010248 field public static final int isScrollContainer = 16843342; // 0x101024e + field public static final int isSplitRequired = 16844176; // 0x1010590 field public static final int isStatic = 16844122; // 0x101055a field public static final int isSticky = 16843335; // 0x1010247 field public static final int isolatedProcess = 16843689; // 0x10103a9 @@ -21648,7 +21650,7 @@ package android.inputmethodservice { method public android.view.inputmethod.InputConnection getCurrentInputConnection(); method public android.view.inputmethod.EditorInfo getCurrentInputEditorInfo(); method public boolean getCurrentInputStarted(); - method public int getInputMethodWindowRecommendedHeight(); + method public deprecated int getInputMethodWindowRecommendedHeight(); method public android.view.LayoutInflater getLayoutInflater(); method public int getMaxWidth(); method public java.lang.CharSequence getTextForImeAction(int); @@ -32521,6 +32523,7 @@ package android.os { public class Build { ctor public Build(); + method public static java.util.List<android.os.Build.Partition> getPartitions(); method public static java.lang.String getRadioVersion(); method public static java.lang.String getSerial(); field public static final java.lang.String BOARD; @@ -32549,6 +32552,14 @@ package android.os { field public static final java.lang.String USER; } + public static class Build.Partition { + ctor public Build.Partition(); + method public java.lang.String getFingerprint(); + method public java.lang.String getName(); + method public long getTimeMillis(); + field public static final java.lang.String PARTITION_NAME_SYSTEM = "system"; + } + public static class Build.VERSION { ctor public Build.VERSION(); field public static final java.lang.String BASE_OS; @@ -33723,6 +33734,7 @@ package android.os { field public static final java.lang.String DISALLOW_FUN = "no_fun"; field public static final java.lang.String DISALLOW_INSTALL_APPS = "no_install_apps"; field public static final java.lang.String DISALLOW_INSTALL_UNKNOWN_SOURCES = "no_install_unknown_sources"; + field public static final java.lang.String DISALLOW_INSTALL_UNKNOWN_SOURCES_GLOBALLY = "no_install_unknown_sources_globally"; field public static final java.lang.String DISALLOW_MODIFY_ACCOUNTS = "no_modify_accounts"; field public static final java.lang.String DISALLOW_MOUNT_PHYSICAL_MEDIA = "no_physical_media"; field public static final java.lang.String DISALLOW_NETWORK_RESET = "no_network_reset"; @@ -42568,7 +42580,7 @@ package android.telephony { method public android.telephony.mbms.StreamingService startStreaming(android.telephony.mbms.StreamingServiceInfo, java.util.concurrent.Executor, android.telephony.mbms.StreamingServiceCallback); } - public class NeighboringCellInfo implements android.os.Parcelable { + public deprecated class NeighboringCellInfo implements android.os.Parcelable { ctor public deprecated NeighboringCellInfo(); ctor public deprecated NeighboringCellInfo(int, int); ctor public NeighboringCellInfo(int, java.lang.String, int); @@ -43029,7 +43041,6 @@ package android.telephony { method public java.lang.String getMmsUAProfUrl(); method public java.lang.String getMmsUserAgent(); method public java.lang.String getNai(); - method public deprecated java.util.List<android.telephony.NeighboringCellInfo> getNeighboringCellInfo(); method public java.lang.String getNetworkCountryIso(); method public java.lang.String getNetworkOperator(); method public java.lang.String getNetworkOperatorName(); @@ -45696,6 +45707,7 @@ package android.util { method public java.util.Set<java.util.Map.Entry<K, V>> entrySet(); method public V get(java.lang.Object); method public int indexOfKey(java.lang.Object); + method public int indexOfValue(java.lang.Object); method public boolean isEmpty(); method public K keyAt(int); method public java.util.Set<K> keySet(); @@ -45716,6 +45728,7 @@ package android.util { ctor public ArraySet(); ctor public ArraySet(int); ctor public ArraySet(android.util.ArraySet<E>); + ctor public ArraySet(java.util.Collection<? extends E>); method public boolean add(E); method public void addAll(android.util.ArraySet<? extends E>); method public boolean addAll(java.util.Collection<? extends E>); @@ -46287,6 +46300,7 @@ package android.util { method public int keyAt(int); method public void put(int, boolean); method public void removeAt(int); + method public void setValueAt(int, boolean); method public int size(); method public boolean valueAt(int); } @@ -46305,6 +46319,7 @@ package android.util { method public int keyAt(int); method public void put(int, int); method public void removeAt(int); + method public void setValueAt(int, int); method public int size(); method public int valueAt(int); } @@ -50322,7 +50337,7 @@ package android.view.animation { method public long computeDurationHint(); method protected void ensureInterpolator(); method public int getBackgroundColor(); - method public boolean getDetachWallpaper(); + method public deprecated boolean getDetachWallpaper(); method public long getDuration(); method public boolean getFillAfter(); method public boolean getFillBefore(); @@ -50346,7 +50361,7 @@ package android.view.animation { method public void scaleCurrentDuration(float); method public void setAnimationListener(android.view.animation.Animation.AnimationListener); method public void setBackgroundColor(int); - method public void setDetachWallpaper(boolean); + method public deprecated void setDetachWallpaper(boolean); method public void setDuration(long); method public void setFillAfter(boolean); method public void setFillBefore(boolean); diff --git a/api/removed.txt b/api/removed.txt index b6dabcd8614b..f7106d2207ec 100644 --- a/api/removed.txt +++ b/api/removed.txt @@ -545,6 +545,7 @@ package android.telephony { } public class TelephonyManager { + method public deprecated java.util.List<android.telephony.NeighboringCellInfo> getNeighboringCellInfo(); method public deprecated android.telephony.NetworkScan requestNetworkScan(android.telephony.NetworkScanRequest, android.telephony.TelephonyScanManager.NetworkScanCallback); } diff --git a/api/system-current.txt b/api/system-current.txt index 5785e4aa3969..041b49708a6b 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -128,6 +128,7 @@ package android { field public static final java.lang.String PEERS_MAC_ADDRESS = "android.permission.PEERS_MAC_ADDRESS"; field public static final java.lang.String PERFORM_CDMA_PROVISIONING = "android.permission.PERFORM_CDMA_PROVISIONING"; field public static final java.lang.String PERFORM_SIM_ACTIVATION = "android.permission.PERFORM_SIM_ACTIVATION"; + field public static final java.lang.String POWER_SAVER = "android.permission.POWER_SAVER"; field public static final java.lang.String PROVIDE_RESOLVER_RANKER_SERVICE = "android.permission.PROVIDE_RESOLVER_RANKER_SERVICE"; field public static final java.lang.String PROVIDE_TRUST_AGENT = "android.permission.PROVIDE_TRUST_AGENT"; field public static final java.lang.String QUERY_TIME_ZONE_RULES = "android.permission.QUERY_TIME_ZONE_RULES"; @@ -3995,6 +3996,7 @@ package android.os { } public final class PowerManager { + method public boolean setPowerSaveMode(boolean); method public void userActivity(long, int, int); field public static final int USER_ACTIVITY_EVENT_ACCESSIBILITY = 3; // 0x3 field public static final int USER_ACTIVITY_EVENT_BUTTON = 1; // 0x1 @@ -4320,13 +4322,6 @@ package android.provider { field public static final java.lang.String STATE = "state"; } - public static final class ContactsContract.RawContacts implements android.provider.BaseColumns android.provider.ContactsContract.ContactNameColumns android.provider.ContactsContract.ContactOptionsColumns android.provider.ContactsContract.RawContactsColumns android.provider.ContactsContract.SyncColumns { - field public static final android.net.Uri RAW_CONTACTS_NOTIFICATION_DELETE_URI; - field public static final android.net.Uri RAW_CONTACTS_NOTIFICATION_INSERT_URI; - field public static final android.net.Uri RAW_CONTACTS_NOTIFICATION_UPDATE_URI; - field public static final android.net.Uri RAW_CONTACTS_NOTIFICATION_URI; - } - public abstract class SearchIndexableData { ctor public SearchIndexableData(); ctor public SearchIndexableData(android.content.Context); diff --git a/api/test-current.txt b/api/test-current.txt index 956761615254..dd02504e37b1 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -708,6 +708,10 @@ package android.os { method public void removeSyncBarrier(int); } + public final class PowerManager { + method public boolean setPowerSaveMode(boolean); + } + public class Process { method public static final int getThreadScheduler(int) throws java.lang.IllegalArgumentException; } @@ -932,13 +936,6 @@ package android.provider { field public static final android.net.Uri ENTERPRISE_CONTENT_URI; } - public static final class ContactsContract.RawContacts implements android.provider.BaseColumns android.provider.ContactsContract.ContactNameColumns android.provider.ContactsContract.ContactOptionsColumns android.provider.ContactsContract.RawContactsColumns android.provider.ContactsContract.SyncColumns { - field public static final android.net.Uri RAW_CONTACTS_NOTIFICATION_DELETE_URI; - field public static final android.net.Uri RAW_CONTACTS_NOTIFICATION_INSERT_URI; - field public static final android.net.Uri RAW_CONTACTS_NOTIFICATION_UPDATE_URI; - field public static final android.net.Uri RAW_CONTACTS_NOTIFICATION_URI; - } - public static final class ContactsContract.RawContactsEntity implements android.provider.BaseColumns android.provider.ContactsContract.DataColumns android.provider.ContactsContract.RawContactsColumns { field public static final android.net.Uri CORP_CONTENT_URI; } diff --git a/cmds/statsd/Android.mk b/cmds/statsd/Android.mk index e090ed14320c..f6b0db80f3ad 100644 --- a/cmds/statsd/Android.mk +++ b/cmds/statsd/Android.mk @@ -228,7 +228,8 @@ LOCAL_SRC_FILES := \ tests/e2e/Anomaly_count_e2e_test.cpp \ tests/e2e/Anomaly_duration_sum_e2e_test.cpp \ tests/e2e/ConfigTtl_e2e_test.cpp \ - tests/e2e/PartialBucket_e2e_test.cpp + tests/e2e/PartialBucket_e2e_test.cpp \ + tests/shell/ShellSubscriber_test.cpp LOCAL_STATIC_LIBRARIES := \ $(statsd_common_static_libraries) \ diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto index 30d8bfce5d2a..988ffc4d95b7 100644 --- a/cmds/statsd/src/atoms.proto +++ b/cmds/statsd/src/atoms.proto @@ -178,6 +178,7 @@ message Atom { BatteryVoltage battery_voltage = 10030; NumFingerprints num_fingerprints = 10031; ProcStats proc_stats = 10029; + DiskIo disk_io = 10032; } // DO NOT USE field numbers above 100,000 in AOSP. @@ -2622,6 +2623,30 @@ message CategorySize { } /** + * Pulls per uid I/O stats. The stats are cumulative since boot. + * + * Read/write bytes are I/O events from a storage device + * Read/write chars are data requested by read/write syscalls, and can be + * satisfied by caching. + * + * Pulled from StatsCompanionService, which reads proc/uid_io/stats. + */ +message DiskIo { + optional int32 uid = 1 [(is_uid) = true]; + optional int64 fg_chars_read = 2; + optional int64 fg_chars_write = 3; + optional int64 fg_bytes_read = 4; + optional int64 fg_bytes_write = 5; + optional int64 bg_chars_read = 6; + optional int64 bg_chars_write = 7; + optional int64 bg_bytes_read = 8; + optional int64 bg_bytes_write = 9; + optional int64 fg_fsync = 10; + optional int64 bg_fsync= 11; +} + + +/** * Pulls the number of fingerprints for each user. * * Pulled from StatsCompanionService, which queries FingerprintManager. diff --git a/cmds/statsd/src/external/StatsPullerManager.cpp b/cmds/statsd/src/external/StatsPullerManager.cpp index 66392f80f1fe..fd8671406051 100644 --- a/cmds/statsd/src/external/StatsPullerManager.cpp +++ b/cmds/statsd/src/external/StatsPullerManager.cpp @@ -163,10 +163,7 @@ const std::map<int, PullAtomInfo> StatsPullerManager::kAllPullAtomInfo = { new ResourceHealthManagerPuller(android::util::FULL_BATTERY_CAPACITY)}}, // battery_voltage {android::util::BATTERY_VOLTAGE, - {{}, - {}, - 1 * NS_PER_SEC, - new ResourceHealthManagerPuller(android::util::BATTERY_VOLTAGE)}}, + {{}, {}, 1 * NS_PER_SEC, new ResourceHealthManagerPuller(android::util::BATTERY_VOLTAGE)}}, // process_memory_state {android::util::PROCESS_MEMORY_STATE, {{4, 5, 6, 7, 8, 9}, @@ -204,17 +201,23 @@ const std::map<int, PullAtomInfo> StatsPullerManager::kAllPullAtomInfo = { {{}, {}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::APP_SIZE)}}, // Size of specific categories of files. Eg. Music. {android::util::CATEGORY_SIZE, - {{}, - {}, - 1 * NS_PER_SEC, - new StatsCompanionServicePuller(android::util::CATEGORY_SIZE)}}, + {{}, {}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::CATEGORY_SIZE)}}, // Number of fingerprints registered to each user. {android::util::NUM_FINGERPRINTS, {{}, {}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::NUM_FINGERPRINTS)}}, - }; + // ProcStats. + {android::util::PROC_STATS, + {{}, {}, 1 * NS_PER_SEC, new StatsCompanionServicePuller(android::util::PROC_STATS)}}, + // Disk I/O stats per uid. + {android::util::DISK_IO, + {{2,3,4,5,6,7,8,9,10,11}, + {}, + 3 * NS_PER_SEC, + new StatsCompanionServicePuller(android::util::DISK_IO)}}, +}; StatsPullerManager::StatsPullerManager() : mNextPullTimeNs(NO_ALARM_UPDATE) { } diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.cpp b/cmds/statsd/src/metrics/DurationMetricProducer.cpp index 9d9e5be9e165..dd3402dae2f8 100644 --- a/cmds/statsd/src/metrics/DurationMetricProducer.cpp +++ b/cmds/statsd/src/metrics/DurationMetricProducer.cpp @@ -781,7 +781,7 @@ void DurationMetricProducer::onMatchedLogEventLocked(const size_t matcherIndex, if (whatIt != mCurrentSlicedDurationTrackerMap.end()) { for (const auto& condIt : whatIt->second) { const bool cond = dimensionKeysInCondition.find(condIt.first) != - dimensionKeysInCondition.end(); + dimensionKeysInCondition.end() && condition; handleStartEvent(MetricDimensionKey(dimensionInWhat, condIt.first), conditionKey, cond, event); dimensionKeysInCondition.erase(condIt.first); diff --git a/cmds/statsd/src/shell/ShellSubscriber.cpp b/cmds/statsd/src/shell/ShellSubscriber.cpp index 3cd49d722fea..1306a467e5c4 100644 --- a/cmds/statsd/src/shell/ShellSubscriber.cpp +++ b/cmds/statsd/src/shell/ShellSubscriber.cpp @@ -113,12 +113,12 @@ void ShellSubscriber::onLogEvent(const LogEvent& event) { for (const auto& matcher : mPushedMatchers) { if (matchesSimple(*mUidMap, matcher, event)) { + event.ToProto(mProto); // First write the payload size. size_t bufferSize = mProto.size(); write(mOutput, &bufferSize, sizeof(bufferSize)); // Then write the payload. - event.ToProto(mProto); mProto.flush(mOutput); mProto.clear(); break; @@ -137,4 +137,4 @@ void ShellSubscriber::binderDied(const wp<IBinder>& who) { } // namespace statsd } // namespace os -} // namespace android
\ No newline at end of file +} // namespace android diff --git a/cmds/statsd/tests/e2e/DimensionInCondition_e2e_combination_AND_cond_test.cpp b/cmds/statsd/tests/e2e/DimensionInCondition_e2e_combination_AND_cond_test.cpp index f03821432cc1..75bd40f67946 100644 --- a/cmds/statsd/tests/e2e/DimensionInCondition_e2e_combination_AND_cond_test.cpp +++ b/cmds/statsd/tests/e2e/DimensionInCondition_e2e_combination_AND_cond_test.cpp @@ -81,6 +81,34 @@ StatsdConfig CreateDurationMetricConfig_NoLink_AND_CombinationCondition( } // namespace +/* + The following test has the following input. + +{ 10000000002 10000000002 (8)9999[I], [S], job0[S], 1[I], } +{ 10000000010 10000000010 (7)111[I], App1[S], 222[I], GMSCoreModule1[S], 222[I], GMSCoreModule2[S], ReadEmail[S], 1[I], } +{ 10000000011 10000000011 (29)1[I], } +{ 10000000040 10000000040 (29)2[I], } +{ 10000000050 10000000050 (7)111[I], App1[S], 222[I], GMSCoreModule1[S], 222[I], GMSCoreModule2[S], ReadEmail[S], 0[I], } +{ 10000000101 10000000101 (8)9999[I], [S], job0[S], 0[I], } +{ 10000000102 10000000102 (29)1[I], } +{ 10000000200 10000000200 (7)111[I], App1[S], 222[I], GMSCoreModule1[S], 222[I], GMSCoreModule2[S], ReadEmail[S], 1[I], } +{ 10000000201 10000000201 (8)9999[I], [S], job2[S], 1[I], } +{ 10000000400 10000000400 (7)111[I], App1[S], 222[I], GMSCoreModule1[S], 222[I], GMSCoreModule2[S], ReadDoc[S], 1[I], } +{ 10000000401 10000000401 (7)333[I], App2[S], 222[I], GMSCoreModule1[S], 555[I], GMSCoreModule2[S], ReadEmail[S], 1[I], } +{ 10000000450 10000000450 (29)2[I], } +{ 10000000500 10000000500 (8)9999[I], [S], job2[S], 0[I], } +{ 10000000600 10000000600 (8)8888[I], [S], job2[S], 1[I], } +{ 10000000650 10000000650 (29)1[I], } +{ 309999999999 309999999999 (7)111[I], App1[S], 222[I], GMSCoreModule1[S], 222[I], GMSCoreModule2[S], ReadDoc[S], 0[I], } +{ 310000000100 310000000100 (29)2[I], } +{ 310000000300 310000000300 (7)111[I], App1[S], 222[I], GMSCoreModule1[S], 222[I], GMSCoreModule2[S], ReadEmail[S], 0[I], } +{ 310000000600 310000000600 (8)8888[I], [S], job1[S], 1[I], } +{ 310000000640 310000000640 (29)1[I], } +{ 310000000650 310000000650 (29)2[I], } +{ 310000000700 310000000700 (7)333[I], App2[S], 222[I], GMSCoreModule1[S], 555[I], GMSCoreModule2[S], ReadEmail[S], 0[I], } +{ 310000000850 310000000850 (8)8888[I], [S], job2[S], 0[I], } +{ 310000000900 310000000900 (8)8888[I], [S], job1[S], 0[I], } +*/ TEST(DimensionInConditionE2eTest, TestDurationMetric_NoLink_AND_CombinationCondition) { for (const bool hashStringInReport : { true, false }) { for (bool isDimensionInConditionSubSetOfConditionTrackerDimension : { true, false }) { @@ -250,7 +278,7 @@ TEST(DimensionInConditionE2eTest, TestDurationMetric_NoLink_AND_CombinationCondi EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs); EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(), bucketStartTimeNs + bucketSizeNs); - EXPECT_EQ(data.bucket_info(0).duration_nanos(), 450 - 201 + bucketSizeNs - 600); + EXPECT_EQ(data.bucket_info(0).duration_nanos(), 450 - 201 + bucketSizeNs - 650); EXPECT_EQ(data.bucket_info(1).duration_nanos(), 100); EXPECT_EQ(data.bucket_info(1).start_bucket_elapsed_nanos(), bucketStartTimeNs + bucketSizeNs); @@ -269,7 +297,7 @@ TEST(DimensionInConditionE2eTest, TestDurationMetric_NoLink_AND_CombinationCondi data.dimensions_in_condition(), android::util::SYNC_STATE_CHANGED, 333, "App2"); EXPECT_EQ(data.bucket_info_size(), 2); - EXPECT_EQ(data.bucket_info(0).duration_nanos(), 450 - 401 + bucketSizeNs - 600); + EXPECT_EQ(data.bucket_info(0).duration_nanos(), 450 - 401 + bucketSizeNs - 650); EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs); EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(), bucketStartTimeNs + bucketSizeNs); @@ -331,7 +359,7 @@ TEST(DimensionInConditionE2eTest, TestDurationMetric_NoLink_AND_CombinationCondi EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(), bucketStartTimeNs + bucketSizeNs); EXPECT_EQ(data.bucket_info(0).duration_nanos(), 450 - 201); - EXPECT_EQ(data.bucket_info(1).duration_nanos(), bucketSizeNs - 600 + 100); + EXPECT_EQ(data.bucket_info(1).duration_nanos(), bucketSizeNs - 650 + 100); EXPECT_EQ(data.bucket_info(1).start_bucket_elapsed_nanos(), bucketStartTimeNs + bucketSizeNs); EXPECT_EQ(data.bucket_info(1).end_bucket_elapsed_nanos(), @@ -353,7 +381,7 @@ TEST(DimensionInConditionE2eTest, TestDurationMetric_NoLink_AND_CombinationCondi EXPECT_EQ(data.bucket_info(0).start_bucket_elapsed_nanos(), bucketStartTimeNs); EXPECT_EQ(data.bucket_info(0).end_bucket_elapsed_nanos(), bucketStartTimeNs + bucketSizeNs); - EXPECT_EQ(data.bucket_info(1).duration_nanos(), bucketSizeNs - 600 + 110); + EXPECT_EQ(data.bucket_info(1).duration_nanos(), bucketSizeNs - 650 + 110); EXPECT_EQ(data.bucket_info(1).start_bucket_elapsed_nanos(), bucketStartTimeNs + bucketSizeNs); EXPECT_EQ(data.bucket_info(1).end_bucket_elapsed_nanos(), diff --git a/cmds/statsd/tests/shell/ShellSubscriber_test.cpp b/cmds/statsd/tests/shell/ShellSubscriber_test.cpp new file mode 100644 index 000000000000..b380b03e28d0 --- /dev/null +++ b/cmds/statsd/tests/shell/ShellSubscriber_test.cpp @@ -0,0 +1,136 @@ +// 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. + +#include <gtest/gtest.h> + +#include <unistd.h> +#include "frameworks/base/cmds/statsd/src/atoms.pb.h" +#include "frameworks/base/cmds/statsd/src/shell/shell_config.pb.h" +#include "src/shell/ShellSubscriber.h" +#include "tests/metrics/metrics_test_helper.h" + +#include <stdio.h> +#include <vector> + +using namespace android::os::statsd; +using android::sp; +using std::vector; +using testing::NaggyMock; + +#ifdef __ANDROID__ + +class MyResultReceiver : public BnResultReceiver { +public: + Mutex mMutex; + Condition mCondition; + bool mHaveResult = false; + int32_t mResult = 0; + + virtual void send(int32_t resultCode) { + AutoMutex _l(mMutex); + mResult = resultCode; + mHaveResult = true; + mCondition.signal(); + } + + int32_t waitForResult() { + AutoMutex _l(mMutex); + mCondition.waitRelative(mMutex, 1000000000); + return mResult; + } +}; + +TEST(ShellSubscriberTest, testPushedSubscription) { + // set up 2 pipes for read/write config and data + int fds_config[2]; + ASSERT_EQ(0, pipe(fds_config)); + + int fds_data[2]; + ASSERT_EQ(0, pipe(fds_data)); + + // create a simple config to get screen events + ShellSubscription config; + config.add_pushed()->set_atom_id(29); + + size_t bufferSize = config.ByteSize(); + + // write the config to pipe, first write size of the config + vector<uint8_t> size_buffer(sizeof(bufferSize)); + std::memcpy(size_buffer.data(), &bufferSize, sizeof(bufferSize)); + write(fds_config[1], &bufferSize, sizeof(bufferSize)); + // then write config itself + vector<uint8_t> buffer(bufferSize); + config.SerializeToArray(&buffer[0], bufferSize); + write(fds_config[1], buffer.data(), bufferSize); + close(fds_config[1]); + + // create a shell subscriber. + sp<MockUidMap> uidMap = new NaggyMock<MockUidMap>(); + sp<ShellSubscriber> shellClient = new ShellSubscriber(uidMap); + sp<MyResultReceiver> resultReceiver = new MyResultReceiver(); + + LogEvent event1(29, 1000); + event1.write(2); + event1.init(); + + // mimic a binder thread that a shell subscriber runs on. it would block. + std::thread reader([&resultReceiver, &fds_config, &fds_data, &shellClient] { + shellClient->startNewSubscription(fds_config[0], fds_data[1], resultReceiver); + }); + reader.detach(); + + // let the shell subscriber to receive the config from pipe. + std::this_thread::sleep_for(100ms); + + // send a log event that matches the config. + std::thread log_reader([&shellClient, &event1] { shellClient->onLogEvent(event1); }); + log_reader.detach(); + + if (log_reader.joinable()) { + log_reader.join(); + } + + // wait for the data to be written. + std::this_thread::sleep_for(100ms); + + // this is the expected screen event atom. + Atom atom; + atom.mutable_screen_state_changed()->set_state( + ::android::view::DisplayStateEnum::DISPLAY_STATE_ON); + + int atom_size = atom.ByteSize(); + + // now read from the pipe. firstly read the atom size. + size_t dataSize = 0; + EXPECT_EQ((int)sizeof(dataSize), read(fds_data[0], &dataSize, sizeof(dataSize))); + EXPECT_EQ(atom_size, (int)dataSize); + + // then read that much data which is the atom in proto binary format + vector<uint8_t> dataBuffer(dataSize); + EXPECT_EQ((int)dataSize, read(fds_data[0], dataBuffer.data(), dataSize)); + + // make sure the received bytes can be parsed to an atom + Atom receivedAtom; + EXPECT_TRUE(receivedAtom.ParseFromArray(dataBuffer.data(), dataSize) != 0); + + // serialze the expected atom to bytes. and compare. to make sure they are the same. + vector<uint8_t> atomBuffer(atom_size); + atom.SerializeToArray(&atomBuffer[0], atom_size); + EXPECT_EQ(atomBuffer, dataBuffer); + close(fds_data[0]); +} + +#else +GTEST_LOG_(INFO) << "This test does nothing.\n"; +#endif diff --git a/config/hiddenapi-light-greylist.txt b/config/hiddenapi-light-greylist.txt index ac16fd311c32..6af34f9151b9 100644 --- a/config/hiddenapi-light-greylist.txt +++ b/config/hiddenapi-light-greylist.txt @@ -867,7 +867,6 @@ Landroid/os/PowerManager;->GO_TO_SLEEP_REASON_TIMEOUT:I Landroid/os/PowerManager;->isLightDeviceIdleMode()Z Landroid/os/PowerManager;->mHandler:Landroid/os/Handler; Landroid/os/PowerManager;->mService:Landroid/os/IPowerManager; -Landroid/os/PowerManager;->setPowerSaveMode(Z)Z Landroid/os/PowerManager;->validateWakeLockParameters(ILjava/lang/String;)V Landroid/os/PowerManager;->wakeUp(JLjava/lang/String;)V Landroid/os/Process;->BLUETOOTH_UID:I diff --git a/core/java/android/accounts/AbstractAccountAuthenticator.java b/core/java/android/accounts/AbstractAccountAuthenticator.java index a3b3a9f21954..79d1361192e3 100644 --- a/core/java/android/accounts/AbstractAccountAuthenticator.java +++ b/core/java/android/accounts/AbstractAccountAuthenticator.java @@ -17,7 +17,6 @@ package android.accounts; import android.Manifest; -import android.annotation.SystemApi; import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; @@ -32,8 +31,8 @@ import java.util.Arrays; /** * Abstract base class for creating AccountAuthenticators. - * In order to be an authenticator one must extend this class, provider implementations for the - * abstract methods and write a service that returns the result of {@link #getIBinder()} + * In order to be an authenticator one must extend this class, provide implementations for the + * abstract methods, and write a service that returns the result of {@link #getIBinder()} * in the service's {@link android.app.Service#onBind(android.content.Intent)} when invoked * with an intent with action {@link AccountManager#ACTION_AUTHENTICATOR_INTENT}. This service * must specify the following intent filter and metadata tags in its AndroidManifest.xml file diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index 2acae1cf8bac..482ef2d1a6fd 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -394,7 +394,7 @@ import java.util.List; * <td>The final call you receive before your * activity is destroyed. This can happen either because the * activity is finishing (someone called {@link Activity#finish} on - * it, or because the system is temporarily destroying this + * it), or because the system is temporarily destroying this * instance of the activity to save space. You can distinguish * between these two scenarios with the {@link * Activity#isFinishing} method.</td> @@ -1985,7 +1985,7 @@ public class Activity extends ContextThemeWrapper /** * Perform any final cleanup before an activity is destroyed. This can * happen either because the activity is finishing (someone called - * {@link #finish} on it, or because the system is temporarily destroying + * {@link #finish} on it), or because the system is temporarily destroying * this instance of the activity to save space. You can distinguish * between these two scenarios with the {@link #isFinishing} method. * diff --git a/core/java/android/app/SmsAppService.java b/core/java/android/app/SmsAppService.java index 3f2b025016df..3829d7103b07 100644 --- a/core/java/android/app/SmsAppService.java +++ b/core/java/android/app/SmsAppService.java @@ -24,21 +24,42 @@ import android.os.IBinder; * it so that the process is always running, which allows the app to have a persistent connection * to the server. * - * <p>The service must have {@link android.telephony.TelephonyManager#ACTION_SMS_APP_SERVICE} + * <p>The service must have an {@link android.telephony.TelephonyManager#ACTION_SMS_APP_SERVICE} * action in the intent handler, and be protected with * {@link android.Manifest.permission#BIND_SMS_APP_SERVICE}. However the service does not have to * be exported. * - * <p>Apps can use + * <p>The service must be associated with a non-main process, meaning it must have an + * {@code android:process} tag in its manifest entry. + * + * <p>An app can use * {@link android.content.pm.PackageManager#setComponentEnabledSetting(ComponentName, int, int)} - * to disable/enable the service. Apps should use it to disable the service when it no longer needs - * to be running. + * to disable or enable the service. An app should use it to disable the service when it no longer + * needs to be running. * * <p>When the owner process crashes, the service will be re-bound automatically after a * back-off. * * <p>Note the process may still be killed if the system is under heavy memory pressure, in which * case the process will be re-started later. + * + * <p>Example: First, define a subclass in the application: + * <pre> + * public class MySmsAppService extends SmsAppService { + * } + * </pre> + * Then, declare it in its {@code AndroidManifest.xml}: + * <pre> + * <service + * android:name=".MySmsAppService" + * android:exported="false" + * android:process=":persistent" + * android:permission="android.permission.BIND_SMS_APP_SERVICE"> + * <intent-filter> + * <action android:name="android.telephony.action.SMS_APP_SERVICE" /> + * </intent-filter> + * </service> + * </pre> */ public class SmsAppService extends Service { private final ISmsAppService mImpl; diff --git a/core/java/android/app/StatusBarManager.java b/core/java/android/app/StatusBarManager.java index 2718bfacb618..bf3d885cd9c9 100644 --- a/core/java/android/app/StatusBarManager.java +++ b/core/java/android/app/StatusBarManager.java @@ -17,6 +17,7 @@ package android.app; import android.annotation.IntDef; +import android.annotation.Nullable; import android.annotation.SystemService; import android.annotation.UnsupportedAppUsage; import android.content.Context; @@ -208,10 +209,11 @@ public class StatusBarManager { } /** - * Expand the settings panel and open a subPanel, pass null to just open the settings panel. + * Expand the settings panel and open a subPanel. If the subpanel is null or does not have a + * corresponding tile, the QS panel is simply expanded */ @UnsupportedAppUsage - public void expandSettingsPanel(String subPanel) { + public void expandSettingsPanel(@Nullable String subPanel) { try { final IStatusBarService svc = getService(); if (svc != null) { diff --git a/core/java/android/app/WaitResult.java b/core/java/android/app/WaitResult.java index 898d0cabee3e..5baf2e22bc31 100644 --- a/core/java/android/app/WaitResult.java +++ b/core/java/android/app/WaitResult.java @@ -28,10 +28,10 @@ import java.io.PrintWriter; * @hide */ public class WaitResult implements Parcelable { + public static final int INVALID_DELAY = -1; public int result; public boolean timeout; public ComponentName who; - public long thisTime; public long totalTime; public WaitResult() { @@ -47,7 +47,6 @@ public class WaitResult implements Parcelable { dest.writeInt(result); dest.writeInt(timeout ? 1 : 0); ComponentName.writeToParcel(who, dest); - dest.writeLong(thisTime); dest.writeLong(totalTime); } @@ -68,7 +67,6 @@ public class WaitResult implements Parcelable { result = source.readInt(); timeout = source.readInt() != 0; who = ComponentName.readFromParcel(source); - thisTime = source.readLong(); totalTime = source.readLong(); } @@ -77,7 +75,6 @@ public class WaitResult implements Parcelable { pw.println(prefix + " result=" + result); pw.println(prefix + " timeout=" + timeout); pw.println(prefix + " who=" + who); - pw.println(prefix + " thisTime=" + thisTime); pw.println(prefix + " totalTime=" + totalTime); } }
\ No newline at end of file diff --git a/core/java/android/app/WindowConfiguration.java b/core/java/android/app/WindowConfiguration.java index e6fb5dc02ce3..096c7aa44446 100644 --- a/core/java/android/app/WindowConfiguration.java +++ b/core/java/android/app/WindowConfiguration.java @@ -28,9 +28,13 @@ import android.content.res.Configuration; import android.graphics.Rect; import android.os.Parcel; import android.os.Parcelable; +import android.util.proto.ProtoInputStream; import android.util.proto.ProtoOutputStream; +import android.util.proto.WireTypeMismatchException; import android.view.DisplayInfo; +import java.io.IOException; + /** * Class that contains windowing configuration/state for other objects that contain windows directly * or indirectly. E.g. Activities, Task, Displays, ... @@ -511,6 +515,38 @@ public class WindowConfiguration implements Parcelable, Comparable<WindowConfigu } /** + * Read from a protocol buffer input stream. + * Protocol buffer message definition at {@link android.app.WindowConfigurationProto} + * + * @param proto Stream to read the WindowConfiguration object from. + * @param fieldId Field Id of the WindowConfiguration as defined in the parent message + * @hide + */ + public void readFromProto(ProtoInputStream proto, long fieldId) + throws IOException, WireTypeMismatchException { + final long token = proto.start(fieldId); + try { + while (proto.nextField() != ProtoInputStream.NO_MORE_FIELDS) { + switch (proto.getFieldNumber()) { + case (int) APP_BOUNDS: + mAppBounds = new Rect(); + mAppBounds.readFromProto(proto, APP_BOUNDS); + break; + case (int) WINDOWING_MODE: + mWindowingMode = proto.readInt(WINDOWING_MODE); + break; + case (int) ACTIVITY_TYPE: + mActivityType = proto.readInt(ACTIVITY_TYPE); + break; + } + } + } finally { + // Let caller handle any exceptions + proto.end(token); + } + } + + /** * Returns true if the activities associated with this window configuration display a shadow * around their border. * @hide diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java index fc67c10e7e5e..1839263af5bf 100644 --- a/core/java/android/app/admin/DevicePolicyManager.java +++ b/core/java/android/app/admin/DevicePolicyManager.java @@ -7404,6 +7404,10 @@ public class DevicePolicyManager { * If any app targeting {@link android.os.Build.VERSION_CODES#O} or higher calls this method * with {@link android.provider.Settings.Secure#INSTALL_NON_MARKET_APPS}, * an {@link UnsupportedOperationException} is thrown. + * + * Starting from Android Q, the device and profile owner can also call + * {@link UserManager#DISALLOW_INSTALL_UNKNOWN_SOURCES_GLOBALLY} to restrict unknown sources for + * all users. * </strong> * * @param admin Which {@link DeviceAdminReceiver} this request is associated with. diff --git a/core/java/android/app/usage/UsageEvents.java b/core/java/android/app/usage/UsageEvents.java index 9f22ad193e42..308b39efc3b1 100644 --- a/core/java/android/app/usage/UsageEvents.java +++ b/core/java/android/app/usage/UsageEvents.java @@ -165,6 +165,12 @@ public final class UsageEvents implements Parcelable { */ public static final int KEYGUARD_HIDDEN = 18; + /** + * Keep in sync with the greatest event type value. + * @hide + */ + public static final int MAX_EVENT_TYPE = 18; + /** @hide */ public static final int FLAG_IS_PACKAGE_INSTANT_APP = 1 << 0; @@ -176,6 +182,12 @@ public final class UsageEvents implements Parcelable { public @interface EventFlags {} /** + * Bitwise OR all valid flag constants to create this constant. + * @hide + */ + public static final int VALID_FLAG_BITS = FLAG_IS_PACKAGE_INSTANT_APP; + + /** * {@hide} */ @UnsupportedAppUsage diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index a15711f5da50..3032d164ef46 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -1170,6 +1170,14 @@ public abstract class PackageManager { public static final int INSTALL_FAILED_SANDBOX_VERSION_DOWNGRADE = -27; /** + * Installation return code: this is passed in the {@link PackageInstaller#EXTRA_LEGACY_STATUS} + * if the new package requires at least one split and it was not provided. + * + * @hide + */ + public static final int INSTALL_FAILED_MISSING_SPLIT = -28; + + /** * Installation parse return code: this is passed in the * {@link PackageInstaller#EXTRA_LEGACY_STATUS} if the parser was given a path that is not a * file, or does not end with the expected '.apk' extension. @@ -5927,8 +5935,8 @@ public abstract class PackageManager { case INSTALL_FAILED_DUPLICATE_PERMISSION: return "INSTALL_FAILED_DUPLICATE_PERMISSION"; case INSTALL_FAILED_NO_MATCHING_ABIS: return "INSTALL_FAILED_NO_MATCHING_ABIS"; case INSTALL_FAILED_ABORTED: return "INSTALL_FAILED_ABORTED"; - case INSTALL_FAILED_BAD_DEX_METADATA: - return "INSTALL_FAILED_BAD_DEX_METADATA"; + case INSTALL_FAILED_BAD_DEX_METADATA: return "INSTALL_FAILED_BAD_DEX_METADATA"; + case INSTALL_FAILED_MISSING_SPLIT: return "INSTALL_FAILED_MISSING_SPLIT"; default: return Integer.toString(status); } } @@ -5979,6 +5987,7 @@ public abstract class PackageManager { case INSTALL_FAILED_DUPLICATE_PERMISSION: return PackageInstaller.STATUS_FAILURE_CONFLICT; case INSTALL_FAILED_NO_MATCHING_ABIS: return PackageInstaller.STATUS_FAILURE_INCOMPATIBLE; case INSTALL_FAILED_ABORTED: return PackageInstaller.STATUS_FAILURE_ABORTED; + case INSTALL_FAILED_MISSING_SPLIT: return PackageInstaller.STATUS_FAILURE_INCOMPATIBLE; default: return PackageInstaller.STATUS_FAILURE; } } diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java index 1fa5190ef8df..f5431caaf319 100644 --- a/core/java/android/content/pm/PackageParser.java +++ b/core/java/android/content/pm/PackageParser.java @@ -452,10 +452,12 @@ public class PackageParser { public final boolean use32bitAbi; public final boolean extractNativeLibs; public final boolean isolatedSplits; + public final boolean isSplitRequired; public ApkLite(String codePath, String packageName, String splitName, boolean isFeatureSplit, - String configForSplit, String usesSplitName, int versionCode, int versionCodeMajor, + String configForSplit, String usesSplitName, boolean isSplitRequired, + int versionCode, int versionCodeMajor, int revisionCode, int installLocation, List<VerifierInfo> verifiers, SigningDetails signingDetails, boolean coreApp, boolean debuggable, boolean multiArch, boolean use32bitAbi, @@ -478,6 +480,7 @@ public class PackageParser { this.use32bitAbi = use32bitAbi; this.extractNativeLibs = extractNativeLibs; this.isolatedSplits = isolatedSplits; + this.isSplitRequired = isSplitRequired; } public long getLongVersionCode() { @@ -1695,6 +1698,7 @@ public class PackageParser { boolean extractNativeLibs = true; boolean isolatedSplits = false; boolean isFeatureSplit = false; + boolean isSplitRequired = false; String configForSplit = null; String usesSplitName = null; @@ -1717,6 +1721,8 @@ public class PackageParser { configForSplit = attrs.getAttributeValue(i); } else if (attr.equals("isFeatureSplit")) { isFeatureSplit = attrs.getAttributeBooleanValue(i, false); + } else if (attr.equals("isSplitRequired")) { + isSplitRequired = attrs.getAttributeBooleanValue(i, false); } } @@ -1772,8 +1778,8 @@ public class PackageParser { } return new ApkLite(codePath, packageSplit.first, packageSplit.second, isFeatureSplit, - configForSplit, usesSplitName, versionCode, versionCodeMajor, revisionCode, - installLocation, verifiers, signingDetails, coreApp, debuggable, + configForSplit, usesSplitName, isSplitRequired, versionCode, versionCodeMajor, + revisionCode, installLocation, verifiers, signingDetails, coreApp, debuggable, multiArch, use32bitAbi, extractNativeLibs, isolatedSplits); } diff --git a/core/java/android/content/res/Configuration.java b/core/java/android/content/res/Configuration.java index 121b43275257..799f8e55cd18 100644 --- a/core/java/android/content/res/Configuration.java +++ b/core/java/android/content/res/Configuration.java @@ -46,6 +46,7 @@ import android.annotation.Nullable; import android.annotation.TestApi; import android.annotation.UnsupportedAppUsage; import android.app.WindowConfiguration; +import android.content.LocaleProto; import android.content.pm.ActivityInfo; import android.content.pm.ActivityInfo.Config; import android.os.Build; @@ -54,7 +55,9 @@ import android.os.Parcel; import android.os.Parcelable; import android.text.TextUtils; import android.util.DisplayMetrics; +import android.util.proto.ProtoInputStream; import android.util.proto.ProtoOutputStream; +import android.util.proto.WireTypeMismatchException; import android.view.View; import com.android.internal.util.XmlUtils; @@ -67,6 +70,7 @@ import java.io.IOException; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; +import java.util.List; import java.util.Locale; /** @@ -1086,12 +1090,14 @@ public final class Configuration implements Parcelable, Comparable<Configuration /** * Write to a protocol buffer output stream. * Protocol buffer message definition at {@link android.content.ConfigurationProto} + * Has the option to ignore fields that don't need to be persisted to disk. * * @param protoOutputStream Stream to write the Configuration object to. * @param fieldId Field Id of the Configuration as defined in the parent message + * @param persisted Note if this proto will be persisted to disk * @hide */ - public void writeToProto(ProtoOutputStream protoOutputStream, long fieldId) { + public void writeToProto(ProtoOutputStream protoOutputStream, long fieldId, boolean persisted) { final long token = protoOutputStream.start(fieldId); protoOutputStream.write(FONT_SCALE, fontScale); protoOutputStream.write(MCC, mcc); @@ -1113,13 +1119,137 @@ public final class Configuration implements Parcelable, Comparable<Configuration protoOutputStream.write(SCREEN_HEIGHT_DP, screenHeightDp); protoOutputStream.write(SMALLEST_SCREEN_WIDTH_DP, smallestScreenWidthDp); protoOutputStream.write(DENSITY_DPI, densityDpi); - if (windowConfiguration != null) { + // For persistence, we do not care about window configuration + if (!persisted && windowConfiguration != null) { windowConfiguration.writeToProto(protoOutputStream, WINDOW_CONFIGURATION); } protoOutputStream.end(token); } /** + * Write to a protocol buffer output stream. + * Protocol buffer message definition at {@link android.content.ConfigurationProto} + * + * @param protoOutputStream Stream to write the Configuration object to. + * @param fieldId Field Id of the Configuration as defined in the parent message + * @hide + */ + public void writeToProto(ProtoOutputStream protoOutputStream, long fieldId) { + writeToProto(protoOutputStream, fieldId, false); + } + + /** + * Read from a protocol buffer output stream. + * Protocol buffer message definition at {@link android.content.ConfigurationProto} + * + * @param protoInputStream Stream to read the Configuration object from. + * @param fieldId Field Id of the Configuration as defined in the parent message + * @hide + */ + public void readFromProto(ProtoInputStream protoInputStream, long fieldId) throws IOException { + final long token = protoInputStream.start(fieldId); + final List<Locale> list = new ArrayList(); + try { + while (protoInputStream.nextField() != ProtoInputStream.NO_MORE_FIELDS) { + switch (protoInputStream.getFieldNumber()) { + case (int) FONT_SCALE: + fontScale = protoInputStream.readFloat(FONT_SCALE); + break; + case (int) MCC: + mcc = protoInputStream.readInt(MCC); + break; + case (int) MNC: + mnc = protoInputStream.readInt(MNC); + break; + case (int) LOCALES: + // Parse the Locale here to handle all the repeated Locales + // The LocaleList will be created when the message is completed + final long localeToken = protoInputStream.start(LOCALES); + String language = ""; + String country = ""; + String variant = ""; + try { + while (protoInputStream.nextField() + != ProtoInputStream.NO_MORE_FIELDS) { + switch (protoInputStream.getFieldNumber()) { + case (int) LocaleProto.LANGUAGE: + language = protoInputStream.readString( + LocaleProto.LANGUAGE); + break; + case (int) LocaleProto.COUNTRY: + country = protoInputStream.readString(LocaleProto.COUNTRY); + break; + case (int) LocaleProto.VARIANT: + variant = protoInputStream.readString(LocaleProto.VARIANT); + break; + } + } + } catch (WireTypeMismatchException wtme) { + // rethrow for caller deal with + throw wtme; + } finally { + protoInputStream.end(localeToken); + list.add(new Locale(language, country, variant)); + } + break; + case (int) SCREEN_LAYOUT: + screenLayout = protoInputStream.readInt(SCREEN_LAYOUT); + break; + case (int) COLOR_MODE: + colorMode = protoInputStream.readInt(COLOR_MODE); + break; + case (int) TOUCHSCREEN: + touchscreen = protoInputStream.readInt(TOUCHSCREEN); + break; + case (int) KEYBOARD: + keyboard = protoInputStream.readInt(KEYBOARD); + break; + case (int) KEYBOARD_HIDDEN: + keyboardHidden = protoInputStream.readInt(KEYBOARD_HIDDEN); + break; + case (int) HARD_KEYBOARD_HIDDEN: + hardKeyboardHidden = protoInputStream.readInt(HARD_KEYBOARD_HIDDEN); + break; + case (int) NAVIGATION: + navigation = protoInputStream.readInt(NAVIGATION); + break; + case (int) NAVIGATION_HIDDEN: + navigationHidden = protoInputStream.readInt(NAVIGATION_HIDDEN); + break; + case (int) ORIENTATION: + orientation = protoInputStream.readInt(ORIENTATION); + break; + case (int) UI_MODE: + uiMode = protoInputStream.readInt(UI_MODE); + break; + case (int) SCREEN_WIDTH_DP: + screenWidthDp = protoInputStream.readInt(SCREEN_WIDTH_DP); + break; + case (int) SCREEN_HEIGHT_DP: + screenHeightDp = protoInputStream.readInt(SCREEN_HEIGHT_DP); + break; + case (int) SMALLEST_SCREEN_WIDTH_DP: + smallestScreenWidthDp = protoInputStream.readInt(SMALLEST_SCREEN_WIDTH_DP); + break; + case (int) DENSITY_DPI: + densityDpi = protoInputStream.readInt(DENSITY_DPI); + break; + case (int) WINDOW_CONFIGURATION: + windowConfiguration.readFromProto(protoInputStream, WINDOW_CONFIGURATION); + break; + } + } + } finally { + // Let caller handle any exceptions + if (list.size() > 0) { + //Create the LocaleList from the collected Locales + setLocales(new LocaleList(list.toArray(new Locale[list.size()]))); + } + protoInputStream.end(token); + } + } + + /** * Write full {@link android.content.ResourcesConfigurationProto} to protocol buffer output * stream. * diff --git a/core/java/android/hardware/GeomagneticField.java b/core/java/android/hardware/GeomagneticField.java index 94f2ac085965..0d7b695d7f1d 100644 --- a/core/java/android/hardware/GeomagneticField.java +++ b/core/java/android/hardware/GeomagneticField.java @@ -31,7 +31,7 @@ import java.util.GregorianCalendar; * Android may use a newer version of the model. */ public class GeomagneticField { - // The magnetic field at a given point, in nonoteslas in geodetic + // The magnetic field at a given point, in nanoteslas in geodetic // coordinates. private float mX; private float mY; @@ -278,7 +278,7 @@ public class GeomagneticField { } /** - * @return Horizontal component of the field strength in nonoteslas. + * @return Horizontal component of the field strength in nanoteslas. */ public float getHorizontalStrength() { return (float) Math.hypot(mX, mY); diff --git a/core/java/android/hardware/display/DisplayViewport.java b/core/java/android/hardware/display/DisplayViewport.java index 496f34c0348a..df0d46be1354 100644 --- a/core/java/android/hardware/display/DisplayViewport.java +++ b/core/java/android/hardware/display/DisplayViewport.java @@ -16,9 +16,14 @@ package android.hardware.display; +import static java.lang.annotation.RetentionPolicy.SOURCE; + +import android.annotation.IntDef; import android.graphics.Rect; import android.text.TextUtils; +import java.lang.annotation.Retention; + /** * Describes how the pixels of physical display device reflects the content of * a logical display. @@ -35,6 +40,10 @@ public final class DisplayViewport { public static final int VIEWPORT_INTERNAL = 1; public static final int VIEWPORT_EXTERNAL = 2; public static final int VIEWPORT_VIRTUAL = 3; + @IntDef(prefix = { "VIEWPORT_" }, value = { + VIEWPORT_INTERNAL, VIEWPORT_EXTERNAL, VIEWPORT_VIRTUAL}) + @Retention(SOURCE) + public @interface ViewportType {}; // True if this viewport is valid. public boolean valid; @@ -62,6 +71,8 @@ public final class DisplayViewport { // The ID used to uniquely identify this display. public String uniqueId; + public @ViewportType int type; + public void copyFrom(DisplayViewport viewport) { valid = viewport.valid; displayId = viewport.displayId; @@ -71,6 +82,7 @@ public final class DisplayViewport { deviceWidth = viewport.deviceWidth; deviceHeight = viewport.deviceHeight; uniqueId = viewport.uniqueId; + type = viewport.type; } /** @@ -100,7 +112,8 @@ public final class DisplayViewport { && physicalFrame.equals(other.physicalFrame) && deviceWidth == other.deviceWidth && deviceHeight == other.deviceHeight - && TextUtils.equals(uniqueId, other.uniqueId); + && TextUtils.equals(uniqueId, other.uniqueId) + && type == other.type; } @Override @@ -115,13 +128,15 @@ public final class DisplayViewport { result += prime * result + deviceWidth; result += prime * result + deviceHeight; result += prime * result + uniqueId.hashCode(); + result += prime * result + type; return result; } // For debugging purposes. @Override public String toString() { - return "DisplayViewport{valid=" + valid + return "DisplayViewport{type=" + typeToString(type) + + ", valid=" + valid + ", displayId=" + displayId + ", uniqueId='" + uniqueId + "'" + ", orientation=" + orientation @@ -131,4 +146,20 @@ public final class DisplayViewport { + ", deviceHeight=" + deviceHeight + "}"; } + + /** + * Human-readable viewport type. + */ + public static String typeToString(@ViewportType int viewportType) { + switch (viewportType) { + case VIEWPORT_INTERNAL: + return "INTERNAL"; + case VIEWPORT_EXTERNAL: + return "EXTERNAL"; + case VIEWPORT_VIRTUAL: + return "VIRTUAL"; + default: + return "UNKNOWN (" + viewportType + ")"; + } + } } diff --git a/core/java/android/hardware/input/InputManagerInternal.java b/core/java/android/hardware/input/InputManagerInternal.java index c4d7e400795c..d8da548d9bd3 100644 --- a/core/java/android/hardware/input/InputManagerInternal.java +++ b/core/java/android/hardware/input/InputManagerInternal.java @@ -40,8 +40,7 @@ public abstract class InputManagerInternal { * Called by the display manager to set information about the displays as needed * by the input system. The input system must copy this information to retain it. */ - public abstract void setDisplayViewports(DisplayViewport defaultViewport, - DisplayViewport externalTouchViewport, List<DisplayViewport> virtualTouchViewports); + public abstract void setDisplayViewports(List<DisplayViewport> viewports); /** * Called by the power manager to tell the input manager whether it should start diff --git a/core/java/android/hardware/location/ContextHubBroadcastReceiver.java b/core/java/android/hardware/location/ContextHubBroadcastReceiver.java new file mode 100644 index 000000000000..e0cc8b7de634 --- /dev/null +++ b/core/java/android/hardware/location/ContextHubBroadcastReceiver.java @@ -0,0 +1,102 @@ +/* + * Copyright 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.hardware.location; + +import android.app.PendingIntent; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.os.Handler; + +/** + * A BroadcastReceiver that can be used with the Context Hub Service notifications. + * + * @hide + */ +public class ContextHubBroadcastReceiver extends BroadcastReceiver { + // The context at which this receiver operates in + private Context mContext; + + // The handler to post callbacks to when receiving Context Hub Service intents + private Handler mHandler; + + // The callback to be invoked when receiving Context Hub Service intents + private ContextHubClientCallback mCallback; + + // The string to use as the broadcast action for this receiver + private String mAction; + + // True when this receiver is registered to receive Intents, false otherwise + private boolean mRegistered = false; + + public ContextHubBroadcastReceiver(Context context, Handler handler, + ContextHubClientCallback callback, String tag) { + mContext = context; + mHandler = handler; + mCallback = callback; + mAction = tag; + } + + /** + * Registers this receiver to receive Intents from the Context Hub Service. This method must + * only be invoked when the receiver is not registered. + * + * @throws IllegalStateException if the receiver is already registered + */ + public void register() throws IllegalStateException { + if (mRegistered) { + throw new IllegalStateException( + "Cannot register ContextHubBroadcastReceiver multiple times"); + } + IntentFilter intentFilter = new IntentFilter(); + intentFilter.addAction(mAction); + mContext.registerReceiver(this, intentFilter, null /* broadcastPermission */, mHandler); + mRegistered = true; + } + + /** + * Unregisters this receiver. This method must only be invoked if {@link #register()} is + * previously invoked. + * + * @throws IllegalStateException if the receiver is not yet registered + */ + public void unregister() throws IllegalStateException { + if (!mRegistered) { + throw new IllegalStateException( + "Cannot unregister ContextHubBroadcastReceiver when not registered"); + } + mContext.unregisterReceiver(this); + mRegistered = false; + } + + /** + * Creates a new PendingIntent associated with this receiver. + * + * @param flags the flags {@link PendingIntent.Flags} to use for the PendingIntent + * + * @return a PendingIntent to receive notifications for this receiver + */ + public PendingIntent getPendingIntent(@PendingIntent.Flags int flags) { + return PendingIntent.getBroadcast( + mContext, 0 /* requestCode */, new Intent(mAction), flags); + } + + @Override + public void onReceive(Context context, Intent intent) { + // TODO: Implement this + } +} diff --git a/core/java/android/hardware/location/ContextHubClient.java b/core/java/android/hardware/location/ContextHubClient.java index 2335203eb100..917644db4202 100644 --- a/core/java/android/hardware/location/ContextHubClient.java +++ b/core/java/android/hardware/location/ContextHubClient.java @@ -18,6 +18,7 @@ package android.hardware.location; import android.annotation.NonNull; import android.annotation.RequiresPermission; import android.annotation.SystemApi; +import android.app.PendingIntent; import android.os.RemoteException; import com.android.internal.util.Preconditions; @@ -100,6 +101,57 @@ public class ContextHubClient implements Closeable { } /** + * Registers to receive persistent intents for a given nanoapp. + * + * This method should be used if the caller wants to receive notifications even after the + * process exits. The client must have an open connection with the Context Hub Service (i.e. it + * cannot have been closed through the {@link #close()} method). If registered successfully, + * intents will be delivered regarding events for the specified nanoapp from the attached + * Context Hub. Any unicast messages for this client will also be delivered. The intent will + * have an extra {@link #EXTRA_EVENT_TYPE} of type {@link ContextHubManager.Event}, which will + * contain the type of the event. See {@link ContextHubManager.Event} for description of each + * event type. + * + * When the intent is received, this client can be recreated through + * {@link ContextHubManager.createClient(PendingIntent, ContextHubInfo, + * ContextHubClientCallback, Exectutor)}. When recreated, the client can be treated as the + * same endpoint entity from a nanoapp's perspective, and can be continued to be used to send + * messages even if the original process has exited. + * + * Intents will be delivered until it is unregistered through + * {@link #unregisterIntent(PendingIntent)}. Note that the registration of this client will + * continued to be maintained at the Context Hub Service until + * {@link #unregisterIntent(PendingIntent)} is called for registered intents. + * + * See {@link ContextHubBroadcastReceiver} for a helper class to generate the + * {@link PendingIntent} through a {@link BroadcastReceiver}, and maps an {@link Intent} to a + * {@link ContextHubClientCallback}. + * + * @param intent The PendingIntent to register for this client + * @param nanoAppId the unique ID of the nanoapp to receive events for + * @return true on success, false otherwise + * + * @hide + */ + public boolean registerIntent(@NonNull PendingIntent intent, long nanoAppId) { + // TODO: Implement this + return false; + } + + /** + * Unregisters an intent previously registered via {@link #registerIntent(PendingIntent, long)}. + * If this intent has not been registered for this client, this method returns false. + * + * @return true on success, false otherwise + * + * @hide + */ + public boolean unregisterIntent(@NonNull PendingIntent intent) { + // TODO: Implement this + return false; + } + + /** * Sends a message to a nanoapp through the Context Hub Service. * * This function returns RESULT_SUCCESS if the message has reached the HAL, but diff --git a/core/java/android/hardware/location/ContextHubManager.java b/core/java/android/hardware/location/ContextHubManager.java index 12d0531bbf2b..36f3586aec2a 100644 --- a/core/java/android/hardware/location/ContextHubManager.java +++ b/core/java/android/hardware/location/ContextHubManager.java @@ -16,12 +16,14 @@ package android.hardware.location; import android.annotation.CallbackExecutor; +import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.SuppressLint; import android.annotation.SystemApi; import android.annotation.SystemService; +import android.app.PendingIntent; import android.content.Context; import android.os.Handler; import android.os.HandlerExecutor; @@ -33,6 +35,8 @@ import android.util.Log; import com.android.internal.util.Preconditions; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.util.List; import java.util.concurrent.Executor; @@ -49,6 +53,111 @@ import java.util.concurrent.Executor; public final class ContextHubManager { private static final String TAG = "ContextHubManager"; + /** + * An extra of type {@link ContextHubInfo} describing the source of the event. + * + * @hide + */ + public static final String EXTRA_CONTEXT_HUB_INFO = + "android.hardware.location.extra.CONTEXT_HUB_INFO"; + + /** + * An extra of type {@link ContextHubManager.Event} describing the event type. + * + * @hide + */ + public static final String EXTRA_EVENT_TYPE = "android.hardware.location.extra.EVENT_TYPE"; + + /** + * An extra of type long describing the ID of the nanoapp an event is for. + * + * @hide + */ + public static final String EXTRA_NANOAPP_ID = "android.location.hardware.extra.NANOAPP_ID"; + + /** + * An extra of type int describing the nanoapp-specific abort code. + * + * @hide + */ + public static final String EXTRA_NANOAPP_ABORT_CODE = + "android.location.hardware.extra.NANOAPP_ABORT_CODE"; + + /** + * An extra of type {@link NanoAppMessage} describing contents of a message from a nanoapp. + * + * @hide + */ + public static final String EXTRA_MESSAGE = "android.location.hardware.extra.MESSAGE"; + + /** + * Constants describing the type of events from a Context Hub. + * {@hide} + */ + @Retention(RetentionPolicy.SOURCE) + @IntDef(prefix = { "EVENT_" }, value = { + EVENT_NANOAPP_LOADED, + EVENT_NANOAPP_UNLOADED, + EVENT_NANOAPP_ENABLED, + EVENT_NANOAPP_DISABLED, + EVENT_NANOAPP_ABORTED, + EVENT_NANOAPP_MESSAGE, + EVENT_HUB_RESET, + }) + public @interface Event { } + + /** + * An event describing that a nanoapp has been loaded. Contains the EXTRA_NANOAPP_ID extra. + * + * @hide + */ + public static final int EVENT_NANOAPP_LOADED = 0; + + /** + * An event describing that a nanoapp has been unloaded. Contains the EXTRA_NANOAPP_ID extra. + * + * @hide + */ + public static final int EVENT_NANOAPP_UNLOADED = 1; + + /** + * An event describing that a nanoapp has been enabled. Contains the EXTRA_NANOAPP_ID extra. + * + * @hide + */ + public static final int EVENT_NANOAPP_ENABLED = 2; + + /** + * An event describing that a nanoapp has been disabled. Contains the EXTRA_NANOAPP_ID extra. + * + * @hide + */ + public static final int EVENT_NANOAPP_DISABLED = 3; + + /** + * An event describing that a nanoapp has aborted. Contains the EXTRA_NANOAPP_ID and + * EXTRA_NANOAPP_ABORT_CODE extras. + * + * @hide + */ + public static final int EVENT_NANOAPP_ABORTED = 4; + + /** + * An event containing a message sent from a nanoapp. Contains the EXTRA_NANOAPP_ID and + * EXTRA_NANOAPP_MESSAGE extras. + * + * @hide + */ + public static final int EVENT_NANOAPP_MESSAGE = 5; + + /** + * An event describing that the Context Hub has reset. + * + * @hide + */ + public static final int EVENT_HUB_RESET = 6; + + private final Looper mMainLooper; private final IContextHubService mService; private Callback mCallback; @@ -682,6 +791,57 @@ public final class ContextHubManager { } /** + * Creates a ContextHubClient based on an Intent received by the Context Hub Service. + * + * This method is intended to be used after receiving an Intent received as a result of + * {@link ContextHubClient.registerIntent(PendingIntent, long)}, and must have been created + * through {@link #createClient(ContextHubInfo, ContextHubClientCallback, Executor)} or + * equivalent at an earlier time. + * + * @param intent the intent that is associated with a client + * @param hubInfo the hub to attach this client to + * @param callback the notification callback to register + * @param executor the executor to invoke the callback + * @return the registered client object + * + * @throws IllegalArgumentException if hubInfo does not represent a valid hub, or the intent + * was not associated with a client + * @throws IllegalStateException if there were too many registered clients at the service + * @throws NullPointerException if intent, hubInfo, callback, or executor is null + * + * @hide + */ + @NonNull public ContextHubClient createClient( + @NonNull PendingIntent intent, @NonNull ContextHubInfo hubInfo, + @NonNull ContextHubClientCallback callback, + @NonNull @CallbackExecutor Executor executor) { + // TODO: Implement this + throw new UnsupportedOperationException("Not implemented yet"); + } + + /** + * Equivalent to {@link #createClient(Intent, ContextHubInfo, ContextHubClientCallback, + * Executor)} with the executor using the main thread's Looper. + * + * @param intent the intent that is associated with a client + * @param hubInfo the hub to attach this client to + * @param callback the notification callback to register + * @return the registered client object + * + * @throws IllegalArgumentException if hubInfo does not represent a valid hub, or the intent + * was not associated with a client + * @throws IllegalStateException if there were too many registered clients at the service + * @throws NullPointerException if intent, hubInfo, or callback is null + * + * @hide + */ + @NonNull public ContextHubClient createClient( + @NonNull PendingIntent intent, @NonNull ContextHubInfo hubInfo, + @NonNull ContextHubClientCallback callback) { + return createClient(intent, hubInfo, callback, new HandlerExecutor(Handler.getMain())); + } + + /** * Unregister a callback for receive messages from the context hub. * * @see Callback diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java index ae12f93285a8..f7f627ebedc2 100644 --- a/core/java/android/inputmethodservice/InputMethodService.java +++ b/core/java/android/inputmethodservice/InputMethodService.java @@ -2803,18 +2803,22 @@ public class InputMethodService extends AbstractInputMethodService { } /** - * @return The recommended height of the input method window. - * An IME author can get the last input method's height as the recommended height - * by calling this in - * {@link android.inputmethodservice.InputMethodService#onStartInputView(EditorInfo, boolean)}. - * If you don't need to use a predefined fixed height, you can avoid the window-resizing of IME - * switching by using this value as a visible inset height. It's efficient for the smooth - * transition between different IMEs. However, note that this may return 0 (or possibly - * unexpectedly low height). You should thus avoid relying on the return value of this method - * all the time. Please make sure to use a reasonable height for the IME. + * Aimed to return the previous input method's {@link Insets#contentTopInsets}, but its actual + * semantics has never been well defined. + * + * <p>Note that the previous document clearly mentioned that this method could return {@code 0} + * at any time for whatever reason. Now this method is just always returning {@code 0}.</p> + * + * @return on Android {@link android.os.Build.VERSION_CODES#Q} and later devices this method + * always returns {@code 0} + * @deprecated the actual behavior of this method has never been well defined. You cannot use + * this method in a reliable and predictable way */ + @Deprecated public int getInputMethodWindowRecommendedHeight() { - return mImm.getInputMethodWindowVisibleHeight(); + Log.w(TAG, "getInputMethodWindowRecommendedHeight() is deprecated and now always returns 0." + + " Do not use this method."); + return 0; } /** diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java index f2e907833612..8333b817add0 100644 --- a/core/java/android/net/ConnectivityManager.java +++ b/core/java/android/net/ConnectivityManager.java @@ -26,7 +26,6 @@ import android.annotation.UnsupportedAppUsage; import android.app.PendingIntent; import android.content.Context; import android.content.Intent; -import android.content.pm.PackageManager; import android.os.Binder; import android.os.Build.VERSION_CODES; import android.os.Bundle; @@ -3801,8 +3800,9 @@ public class ConnectivityManager { private void unsupportedStartingFrom(int version) { if (Process.myUid() == Process.SYSTEM_UID) { - // The getApplicationInfo() call we make below is not supported in system context, and - // we want to allow the system to use these APIs anyway. + // The getApplicationInfo() call we make below is not supported in system context. Let + // the call through here, and rely on the fact that ConnectivityService will refuse to + // allow the system to use these APIs anyway. return; } @@ -3819,11 +3819,6 @@ public class ConnectivityManager { // functions by accessing ConnectivityService directly. However, it should be clear that doing // so is unsupported and may break in the future. http://b/22728205 private void checkLegacyRoutingApiAccess() { - if (mContext.checkCallingOrSelfPermission("com.android.permission.INJECT_OMADM_SETTINGS") - == PackageManager.PERMISSION_GRANTED) { - return; - } - unsupportedStartingFrom(VERSION_CODES.M); } diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java index 6bd2e76cdf35..8681893702b4 100644 --- a/core/java/android/os/Build.java +++ b/core/java/android/os/Build.java @@ -30,6 +30,8 @@ import com.android.internal.telephony.TelephonyProperties; import dalvik.system.VMRuntime; +import java.util.ArrayList; +import java.util.List; import java.util.Objects; /** @@ -1083,7 +1085,67 @@ public class Build { return true; } + /** Build information for a particular device partition. */ + public static class Partition { + /** The name identifying the system partition. */ + public static final String PARTITION_NAME_SYSTEM = "system"; + + private String mName; + private String mFingerprint; + private long mTimeMs; + + public Partition() {} + + private Partition(String name, String fingerprint, long timeMs) { + mName = name; + mFingerprint = fingerprint; + mTimeMs = timeMs; + } + + /** The name of this partition, e.g. "system", or "vendor" */ + public String getName() { + return mName; + } + + /** The build fingerprint of this partition, see {@link Build#FINGERPRINT}. */ + public String getFingerprint() { + return mFingerprint; + } + + /** The time (ms since epoch), at which this partition was built, see {@link Build#TIME}. */ + public long getTimeMillis() { + return mTimeMs; + } + } + + /** + * Get build information about partitions that have a separate fingerprint defined. + * + * The list includes partitions that are suitable candidates for over-the-air updates. This is + * not an exhaustive list of partitions on the device. + */ + public static List<Partition> getPartitions() { + ArrayList<Partition> partitions = new ArrayList(); + + String[] names = new String[] { + "bootimage", "odm", "product", "product_services", Partition.PARTITION_NAME_SYSTEM, + "vendor" + }; + for (String name : names) { + String fingerprint = SystemProperties.get("ro." + name + ".build.fingerprint"); + if (TextUtils.isEmpty(fingerprint)) { + continue; + } + long time = getLong("ro." + name + ".build.date.utc") * 1000; + partitions.add(new Partition(name, fingerprint, time)); + } + + return partitions; + } + // The following properties only make sense for internal engineering builds. + + /** The time at which the build was produced, given in milliseconds since the UNIX epoch. */ public static final long TIME = getLong("ro.build.date.utc") * 1000; public static final String USER = getString("ro.build.user"); public static final String HOST = getString("ro.build.host"); diff --git a/core/java/android/os/GraphicsEnvironment.java b/core/java/android/os/GraphicsEnvironment.java index f83acb6b7972..54be6393e651 100644 --- a/core/java/android/os/GraphicsEnvironment.java +++ b/core/java/android/os/GraphicsEnvironment.java @@ -45,6 +45,7 @@ public class GraphicsEnvironment { private static final String TAG = "GraphicsEnvironment"; private static final String PROPERTY_GFX_DRIVER = "ro.gfx.driver.0"; private static final String ANGLE_PACKAGE_NAME = "com.android.angle"; + private static final String GLES_MODE_METADATA_KEY = "com.android.angle.GLES_MODE"; private ClassLoader mClassLoader; private String mLayerPath; @@ -123,7 +124,6 @@ public class GraphicsEnvironment { } } } - } // Include the app's lib directory in all cases @@ -133,7 +133,7 @@ public class GraphicsEnvironment { } /** - * Selectively enable ANGLE for applications + * Pass ANGLE details down to trigger enable logic */ private static void setupAngle(Context context) { @@ -143,39 +143,67 @@ public class GraphicsEnvironment { String packageName = context.getPackageName(); - // Only provide an ANGLE namespace if the package name matches setting + boolean devOptIn = false; if ((angleEnabledApp != null && packageName != null) && (!angleEnabledApp.isEmpty() && !packageName.isEmpty()) && angleEnabledApp.equals(packageName)) { - if (DEBUG) Log.v(TAG, "ANGLE enabled for " + packageName); + if (DEBUG) Log.v(TAG, packageName + " opted in for ANGLE via Developer Setting"); - ApplicationInfo angleInfo; + devOptIn = true; + } - try { - angleInfo = context.getPackageManager().getApplicationInfo(ANGLE_PACKAGE_NAME, - PackageManager.MATCH_SYSTEM_ONLY); - } catch (PackageManager.NameNotFoundException e) { - Log.w(TAG, "ANGLE package '" + ANGLE_PACKAGE_NAME + "' not installed"); - return; + ApplicationInfo appInfo; + try { + appInfo = context.getPackageManager().getApplicationInfo(packageName, + PackageManager.GET_META_DATA); + } catch (PackageManager.NameNotFoundException e) { + Log.w(TAG, "Failed to get info about current application: " + packageName); + return; + } + + String appPref = "dontcare"; + final BaseBundle metadata = appInfo.metaData; + if (metadata != null) { + final String glesMode = metadata.getString(GLES_MODE_METADATA_KEY); + if (glesMode != null) { + if (glesMode.equals("angle")) { + appPref = "angle"; + if (DEBUG) Log.v(TAG, packageName + " opted for ANGLE via AndroidManifest"); + } else if (glesMode.equals("native")) { + appPref = "native"; + if (DEBUG) Log.v(TAG, packageName + " opted for NATIVE via AndroidManifest"); + } else { + Log.w(TAG, "Unrecognized GLES_MODE (\"" + glesMode + "\") for " + packageName + + ". Supported values are \"angle\" or \"native\""); + } } + } - String abi = chooseAbi(angleInfo); + ApplicationInfo angleInfo; + try { + angleInfo = context.getPackageManager().getApplicationInfo(ANGLE_PACKAGE_NAME, + PackageManager.MATCH_SYSTEM_ONLY); + } catch (PackageManager.NameNotFoundException e) { + Log.w(TAG, "ANGLE package '" + ANGLE_PACKAGE_NAME + "' not installed"); + return; + } - // Build a path that includes installed native libs and APK - StringBuilder sb = new StringBuilder(); - sb.append(angleInfo.nativeLibraryDir) - .append(File.pathSeparator) - .append(angleInfo.sourceDir) - .append("!/lib/") - .append(abi); - String paths = sb.toString(); + String abi = chooseAbi(angleInfo); - if (DEBUG) Log.v(TAG, "ANGLE package libs: " + paths); + // Build a path that includes installed native libs and APK + StringBuilder sb = new StringBuilder(); + sb.append(angleInfo.nativeLibraryDir) + .append(File.pathSeparator) + .append(angleInfo.sourceDir) + .append("!/lib/") + .append(abi); + String paths = sb.toString(); - // Providing any path will trigger namespace creation - setAnglePath(paths); - } + if (DEBUG) Log.v(TAG, "ANGLE package libs: " + paths); + + // Further opt-in logic is handled in native, so pass relevant info down + setAngleInfo(paths, packageName, appPref, devOptIn); } /** @@ -266,5 +294,6 @@ public class GraphicsEnvironment { private static native void setLayerPaths(ClassLoader classLoader, String layerPaths); private static native void setDebugLayers(String layers); private static native void setDriverPath(String path); - private static native void setAnglePath(String path); + private static native void setAngleInfo(String path, String appPackage, String appPref, + boolean devOptIn); } diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java index 89a5defd221d..8ea061e4a8d0 100644 --- a/core/java/android/os/PowerManager.java +++ b/core/java/android/os/PowerManager.java @@ -1154,10 +1154,15 @@ public final class PowerManager { * * @return True if the set was allowed. * - * @see #isPowerSaveMode() - * * @hide + * @see #isPowerSaveMode() */ + @SystemApi + @TestApi + @RequiresPermission(anyOf = { + android.Manifest.permission.DEVICE_POWER, + android.Manifest.permission.POWER_SAVER + }) public boolean setPowerSaveMode(boolean mode) { try { return mService.setPowerSaveMode(mode); diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java index b0891050634c..128217001b17 100644 --- a/core/java/android/os/UserManager.java +++ b/core/java/android/os/UserManager.java @@ -256,6 +256,7 @@ public class UserManager { /** * Specifies if a user is disallowed from enabling the * "Unknown Sources" setting, that allows installation of apps from unknown sources. + * Unknown sources exclude adb and special apps such as trusted app stores. * The default value is <code>false</code>. * * <p>Key for user restrictions. @@ -267,6 +268,22 @@ public class UserManager { public static final String DISALLOW_INSTALL_UNKNOWN_SOURCES = "no_install_unknown_sources"; /** + * This restriction is a device-wide version of {@link DISALLOW_INSTALL_UNKNOWN_SOURCES}. + * + * Specifies if all users on the device are disallowed from enabling the + * "Unknown Sources" setting, that allows installation of apps from unknown sources. + * The default value is <code>false</code>. + * + * <p>Key for user restrictions. + * <p>Type: Boolean + * @see DevicePolicyManager#addUserRestriction(ComponentName, String) + * @see DevicePolicyManager#clearUserRestriction(ComponentName, String) + * @see #getUserRestrictions() + */ + public static final String DISALLOW_INSTALL_UNKNOWN_SOURCES_GLOBALLY = + "no_install_unknown_sources_globally"; + + /** * Specifies if a user is disallowed from configuring bluetooth. * This does <em>not</em> restrict the user from turning bluetooth on or off. * The default value is <code>false</code>. @@ -1669,8 +1686,9 @@ public class UserManager { /** * @hide * Returns whether the given user has been disallowed from performing certain actions - * or setting certain settings through UserManager. This method disregards restrictions - * set by device policy. + * or setting certain settings through UserManager (e.g. this type of restriction would prevent + * the guest user from doing certain things, such as making calls). This method disregards + * restrictions set by device policy. * @param restrictionKey the string key representing the restriction * @param userHandle the UserHandle of the user for whom to retrieve the restrictions. */ diff --git a/core/java/android/provider/ContactsContract.java b/core/java/android/provider/ContactsContract.java index f126c49227fb..112329e48703 100644 --- a/core/java/android/provider/ContactsContract.java +++ b/core/java/android/provider/ContactsContract.java @@ -2783,48 +2783,7 @@ public final class ContactsContract { * The content:// style URI for this table, which requests a directory of * raw contact rows matching the selection criteria. */ - public static final Uri CONTENT_URI = - Uri.withAppendedPath(AUTHORITY_URI, "raw_contacts"); - - /** - * The URI to register for all raw contacts change notification. - * - * @hide - */ - @SystemApi - @TestApi - public static final Uri RAW_CONTACTS_NOTIFICATION_URI = - Uri.parse("content://com.android.contacts.raw_contacts"); - - /** - * The URI to register for raw contacts insert notification. - * - * @hide - */ - @SystemApi - @TestApi - public static final Uri RAW_CONTACTS_NOTIFICATION_INSERT_URI = - Uri.withAppendedPath(RAW_CONTACTS_NOTIFICATION_URI, "insert"); - - /** - * The URI to register for raw contacts update notification. - * - * @hide - */ - @SystemApi - @TestApi - public static final Uri RAW_CONTACTS_NOTIFICATION_UPDATE_URI = - Uri.withAppendedPath(RAW_CONTACTS_NOTIFICATION_URI, "update"); - - /** - * The URI to register for raw contacts delete notification. - * - * @hide - */ - @SystemApi - @TestApi - public static final Uri RAW_CONTACTS_NOTIFICATION_DELETE_URI = - Uri.withAppendedPath(RAW_CONTACTS_NOTIFICATION_URI, "delete"); + public static final Uri CONTENT_URI = Uri.withAppendedPath(AUTHORITY_URI, "raw_contacts"); /** * The MIME type of the results from {@link #CONTENT_URI} when a specific diff --git a/core/java/android/provider/DocumentsContract.java b/core/java/android/provider/DocumentsContract.java index ee64ca2b8673..8c40e0e6cb8c 100644 --- a/core/java/android/provider/DocumentsContract.java +++ b/core/java/android/provider/DocumentsContract.java @@ -942,13 +942,26 @@ public final class DocumentsContract { return false; } - /** {@hide} */ + /** + * Test if the given URI represents roots backed by {@link DocumentsProvider}. + * + * @see #buildRootsUri(String) + * + * {@hide} + */ + public static boolean isRootsUri(Context context, @Nullable Uri uri) { + return isRootUri(context, uri, 1 /* pathSize */); + } + + /** + * Test if the given URI represents specific root backed by {@link DocumentsProvider}. + * + * @see #buildRootUri(String, String) + * + * {@hide} + */ public static boolean isRootUri(Context context, @Nullable Uri uri) { - if (isContentUri(uri) && isDocumentsProvider(context, uri.getAuthority())) { - final List<String> paths = uri.getPathSegments(); - return (paths.size() == 2 && PATH_ROOT.equals(paths.get(0))); - } - return false; + return isRootUri(context, uri, 2 /* pathSize */); } /** {@hide} */ @@ -967,6 +980,14 @@ public final class DocumentsContract { return (paths.size() >= 2 && PATH_TREE.equals(paths.get(0))); } + private static boolean isRootUri(Context context, @Nullable Uri uri, int pathSize) { + if (isContentUri(uri) && isDocumentsProvider(context, uri.getAuthority())) { + final List<String> paths = uri.getPathSegments(); + return (paths.size() == pathSize && PATH_ROOT.equals(paths.get(0))); + } + return false; + } + private static boolean isDocumentsProvider(Context context, String authority) { final Intent intent = new Intent(PROVIDER_INTERFACE); final List<ResolveInfo> infos = context.getPackageManager() diff --git a/core/java/android/provider/MediaStore.java b/core/java/android/provider/MediaStore.java index 82459b13a4eb..828fd7386d80 100644 --- a/core/java/android/provider/MediaStore.java +++ b/core/java/android/provider/MediaStore.java @@ -1318,18 +1318,6 @@ public final class MediaStore { } public static final class Media implements AudioColumns { - - private static final String[] EXTERNAL_PATHS; - - static { - String secondary_storage = System.getenv("SECONDARY_STORAGE"); - if (secondary_storage != null) { - EXTERNAL_PATHS = secondary_storage.split(":"); - } else { - EXTERNAL_PATHS = new String[0]; - } - } - /** * Get the content:// style URI for the audio media table on the * given volume. @@ -1343,14 +1331,9 @@ public final class MediaStore { } public static Uri getContentUriForPath(String path) { - for (String ep : EXTERNAL_PATHS) { - if (path.startsWith(ep)) { - return EXTERNAL_CONTENT_URI; - } - } - - return (path.startsWith(Environment.getExternalStorageDirectory().getPath()) ? - EXTERNAL_CONTENT_URI : INTERNAL_CONTENT_URI); + return (path.startsWith( + Environment.getStorageDirectory().getAbsolutePath() + "/") + ? EXTERNAL_CONTENT_URI : INTERNAL_CONTENT_URI); } /** diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index ad8626ce736b..80dbfe5326bd 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -7267,12 +7267,12 @@ public final class Settings { private static final Validator DOZE_DOUBLE_TAP_GESTURE_VALIDATOR = BOOLEAN_VALIDATOR; /** - * Whether the device should pulse on reach gesture. + * Gesture that wakes up the lock screen. * @hide */ - public static final String DOZE_REACH_GESTURE = "doze_reach_gesture"; + public static final String DOZE_WAKE_LOCK_SCREEN_GESTURE = "doze_wake_lock_screen_gesture"; - private static final Validator DOZE_REACH_GESTURE_VALIDATOR = BOOLEAN_VALIDATOR; + private static final Validator DOZE_WAKE_LOCK_SCREEN_GESTURE_VALIDATOR = BOOLEAN_VALIDATOR; /** * Gesture that wakes up the display, showing the ambient version of the status bar. @@ -8193,7 +8193,7 @@ public final class Settings { DOZE_ALWAYS_ON, DOZE_PICK_UP_GESTURE, DOZE_DOUBLE_TAP_GESTURE, - DOZE_REACH_GESTURE, + DOZE_WAKE_LOCK_SCREEN_GESTURE, DOZE_WAKE_SCREEN_GESTURE, NFC_PAYMENT_DEFAULT_COMPONENT, AUTOMATIC_STORAGE_MANAGER_DAYS_TO_RETAIN, @@ -8341,7 +8341,7 @@ public final class Settings { VALIDATORS.put(DOZE_ALWAYS_ON, DOZE_ALWAYS_ON_VALIDATOR); VALIDATORS.put(DOZE_PICK_UP_GESTURE, DOZE_PICK_UP_GESTURE_VALIDATOR); VALIDATORS.put(DOZE_DOUBLE_TAP_GESTURE, DOZE_DOUBLE_TAP_GESTURE_VALIDATOR); - VALIDATORS.put(DOZE_REACH_GESTURE, DOZE_REACH_GESTURE_VALIDATOR); + VALIDATORS.put(DOZE_WAKE_LOCK_SCREEN_GESTURE, DOZE_WAKE_LOCK_SCREEN_GESTURE_VALIDATOR); VALIDATORS.put(DOZE_WAKE_SCREEN_GESTURE, DOZE_WAKE_SCREEN_GESTURE_VALIDATOR); VALIDATORS.put(NFC_PAYMENT_DEFAULT_COMPONENT, NFC_PAYMENT_DEFAULT_COMPONENT_VALIDATOR); VALIDATORS.put(AUTOMATIC_STORAGE_MANAGER_DAYS_TO_RETAIN, @@ -9253,6 +9253,19 @@ public final class Settings { public static final String MOBILE_DATA_ALWAYS_ON = "mobile_data_always_on"; /** + * Whether the wifi data connection should remain active even when higher + * priority networks like Ethernet are active, to keep both networks. + * In the case where higher priority networks are connected, wifi will be + * unused unless an application explicitly requests to use it. + * + * See ConnectivityService for more info. + * + * (0 = disabled, 1 = enabled) + * @hide + */ + public static final String WIFI_ALWAYS_REQUESTED = "wifi_always_requested"; + + /** * Size of the event buffer for IP connectivity metrics. * @hide */ @@ -9604,8 +9617,7 @@ public final class Settings { * Use the old dnsmasq DHCP server for tethering instead of the framework implementation. * * Integer values are interpreted as boolean, and the absence of an explicit setting - * is interpreted as |true|. - * TODO: make the default |false| + * is interpreted as |false|. * @hide */ public static final String TETHER_ENABLE_LEGACY_DHCP_SERVER = @@ -10822,6 +10834,12 @@ public final class Settings { = "activity_starts_logging_enabled"; /** + * @hide + * @see com.android.server.appbinding.AppBindingConstants + */ + public static final String APP_BINDING_CONSTANTS = "app_binding_constants"; + + /** * App ops specific settings. * This is encoded as a key=value list, separated by commas. Ex: * diff --git a/core/java/android/util/ArrayMap.java b/core/java/android/util/ArrayMap.java index 5108a796a036..294a1799a2d3 100644 --- a/core/java/android/util/ArrayMap.java +++ b/core/java/android/util/ArrayMap.java @@ -71,19 +71,19 @@ public final class ArrayMap<K, V> implements Map<K, V> { /** * Maximum number of entries to have in array caches. */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = 28) // Allocations are an implementation detail. private static final int CACHE_SIZE = 10; /** * Special hash array value that indicates the container is immutable. */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = 28) // Allocations are an implementation detail. static final int[] EMPTY_IMMUTABLE_INTS = new int[0]; /** * @hide Special immutable empty ArrayMap. */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = 28) // Use your own singleton empty map. public static final ArrayMap EMPTY = new ArrayMap<>(-1); /** @@ -92,21 +92,21 @@ public final class ArrayMap<K, V> implements Map<K, V> { * The first entry in the array is a pointer to the next array in the * list; the second entry is a pointer to the int[] hash code array for it. */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = 28) // Allocations are an implementation detail. static Object[] mBaseCache; - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = 28) // Allocations are an implementation detail. static int mBaseCacheSize; - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = 28) // Allocations are an implementation detail. static Object[] mTwiceBaseCache; - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = 28) // Allocations are an implementation detail. static int mTwiceBaseCacheSize; final boolean mIdentityHashCode; - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = 28) // Hashes are an implementation detail. Use public key/value API. int[] mHashes; - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = 28) // Storage is an implementation detail. Use public key/value API. Object[] mArray; - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = 28) // Use size() int mSize; MapCollections<K, V> mCollections; @@ -122,7 +122,7 @@ public final class ArrayMap<K, V> implements Map<K, V> { } } - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = 28) // Hashes are an implementation detail. Use indexOfKey(Object). int indexOf(Object key, int hash) { final int N = mSize; @@ -161,7 +161,7 @@ public final class ArrayMap<K, V> implements Map<K, V> { return ~end; } - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = 28) // Use indexOf(null) int indexOfNull() { final int N = mSize; @@ -200,7 +200,7 @@ public final class ArrayMap<K, V> implements Map<K, V> { return ~end; } - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = 28) // Allocations are an implementation detail. private void allocArrays(final int size) { if (mHashes == EMPTY_IMMUTABLE_INTS) { throw new UnsupportedOperationException("ArrayMap is immutable"); @@ -239,7 +239,7 @@ public final class ArrayMap<K, V> implements Map<K, V> { mArray = new Object[size<<1]; } - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = 28) // Allocations are an implementation detail. private static void freeArrays(final int[] hashes, final Object[] array, final int size) { if (hashes.length == (BASE_SIZE*2)) { synchronized (ArrayMap.class) { @@ -393,8 +393,15 @@ public final class ArrayMap<K, V> implements Map<K, V> { : indexOf(key, mIdentityHashCode ? System.identityHashCode(key) : key.hashCode()); } - @UnsupportedAppUsage - int indexOfValue(Object value) { + /** + * Returns an index for which {@link #valueAt} would return the + * specified value, or a negative number if no keys map to the + * specified value. + * Beware that this is a linear search, unlike lookups by key, + * and that multiple keys can map to the same value and this will + * find only one of them. + */ + public int indexOfValue(Object value) { final int N = mSize*2; final Object[] array = mArray; if (value == null) { @@ -551,7 +558,7 @@ public final class ArrayMap<K, V> implements Map<K, V> { * The array must already be large enough to contain the item. * @hide */ - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = 28) // Storage is an implementation detail. Use put(K, V). public void append(K key, V value) { int index = mSize; final int hash = key == null ? 0 diff --git a/core/java/android/util/ArraySet.java b/core/java/android/util/ArraySet.java index 526a950b4820..d74a0fe8d2c1 100644 --- a/core/java/android/util/ArraySet.java +++ b/core/java/android/util/ArraySet.java @@ -71,15 +71,15 @@ public final class ArraySet<E> implements Collection<E>, Set<E> { static int sTwiceBaseCacheSize; final boolean mIdentityHashCode; - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = 28) // Hashes are an implementation detail. Use public API. int[] mHashes; - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = 28) // Storage is an implementation detail. Use public API. Object[] mArray; - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = 28) // Use size() int mSize; MapCollections<E, E> mCollections; - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = 28) // Hashes are an implementation detail. Use indexOfKey(Object). private int indexOf(Object key, int hash) { final int N = mSize; @@ -118,7 +118,7 @@ public final class ArraySet<E> implements Collection<E>, Set<E> { return ~end; } - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = 28) // Use indexOf(null) private int indexOfNull() { final int N = mSize; @@ -157,7 +157,7 @@ public final class ArraySet<E> implements Collection<E>, Set<E> { return ~end; } - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = 28) // Allocations are an implementation detail. private void allocArrays(final int size) { if (size == (BASE_SIZE * 2)) { synchronized (ArraySet.class) { @@ -215,7 +215,7 @@ public final class ArraySet<E> implements Collection<E>, Set<E> { mArray = new Object[size]; } - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = 28) // Allocations are an implementation detail. private static void freeArrays(final int[] hashes, final Object[] array, final int size) { if (hashes.length == (BASE_SIZE * 2)) { synchronized (ArraySet.class) { @@ -289,9 +289,10 @@ public final class ArraySet<E> implements Collection<E>, Set<E> { } } - /** {@hide} */ - @UnsupportedAppUsage - public ArraySet(Collection<E> set) { + /** + * Create a new ArraySet with items from the given collection. + */ + public ArraySet(Collection<? extends E> set) { this(); if (set != null) { addAll(set); diff --git a/core/java/android/util/LongSparseLongArray.java b/core/java/android/util/LongSparseLongArray.java index d5af92234b31..af163ac8f246 100644 --- a/core/java/android/util/LongSparseLongArray.java +++ b/core/java/android/util/LongSparseLongArray.java @@ -46,11 +46,11 @@ import libcore.util.EmptyArray; * @hide */ public class LongSparseLongArray implements Cloneable { - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = 28) // The type isn't even public. private long[] mKeys; - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = 28) // The type isn't even public. private long[] mValues; - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = 28) // The type isn't even public. private int mSize; /** diff --git a/core/java/android/util/SparseArray.java b/core/java/android/util/SparseArray.java index aa5ca3530621..89ea2d35fc2f 100644 --- a/core/java/android/util/SparseArray.java +++ b/core/java/android/util/SparseArray.java @@ -56,11 +56,11 @@ public class SparseArray<E> implements Cloneable { private static final Object DELETED = new Object(); private boolean mGarbage = false; - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = 28) // Use keyAt(int) private int[] mKeys; - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = 28) // Use valueAt(int), setValueAt(int, E) private Object[] mValues; - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = 28) // Use size() private int mSize; /** diff --git a/core/java/android/util/SparseBooleanArray.java b/core/java/android/util/SparseBooleanArray.java index 9c6b9698d1ae..d4c40954bdd1 100644 --- a/core/java/android/util/SparseBooleanArray.java +++ b/core/java/android/util/SparseBooleanArray.java @@ -185,7 +185,9 @@ public class SparseBooleanArray implements Cloneable { return mValues[index]; } - /** @hide */ + /** + * Directly set the value at a particular index. + */ public void setValueAt(int index, boolean value) { mValues[index] = value; } @@ -304,10 +306,10 @@ public class SparseBooleanArray implements Cloneable { return buffer.toString(); } - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = 28) // Use keyAt(int) private int[] mKeys; - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = 28) // Use valueAt(int), setValueAt(int, boolean) private boolean[] mValues; - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = 28) // Use size() private int mSize; } diff --git a/core/java/android/util/SparseIntArray.java b/core/java/android/util/SparseIntArray.java index 19547534aef5..9e6bad1d9ae0 100644 --- a/core/java/android/util/SparseIntArray.java +++ b/core/java/android/util/SparseIntArray.java @@ -46,11 +46,11 @@ import libcore.util.EmptyArray; * order in the case of <code>valueAt(int)</code>.</p> */ public class SparseIntArray implements Cloneable { - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = 28) // Use keyAt(int) private int[] mKeys; - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = 28) // Use valueAt(int), setValueAt(int, int) private int[] mValues; - @UnsupportedAppUsage + @UnsupportedAppUsage(maxTargetSdk = 28) // Use size() private int mSize; /** @@ -191,7 +191,6 @@ public class SparseIntArray implements Cloneable { /** * Directly set the value at a particular index. - * @hide */ public void setValueAt(int index, int value) { mValues[index] = value; diff --git a/core/java/android/util/apk/ApkSignatureSchemeV2Verifier.java b/core/java/android/util/apk/ApkSignatureSchemeV2Verifier.java index 1203541756e8..1bbef8e9cfff 100644 --- a/core/java/android/util/apk/ApkSignatureSchemeV2Verifier.java +++ b/core/java/android/util/apk/ApkSignatureSchemeV2Verifier.java @@ -410,7 +410,7 @@ public class ApkSignatureSchemeV2Verifier { NoSuchAlgorithmException { try (RandomAccessFile apk = new RandomAccessFile(apkPath, "r")) { SignatureInfo signatureInfo = findSignature(apk); - return ApkVerityBuilder.generateApkVerity(apkPath, bufferFactory, signatureInfo); + return VerityBuilder.generateApkVerity(apkPath, bufferFactory, signatureInfo); } } @@ -423,7 +423,7 @@ public class ApkSignatureSchemeV2Verifier { if (vSigner.verityRootHash == null) { return null; } - return ApkVerityBuilder.generateApkVerityRootHash( + return VerityBuilder.generateApkVerityRootHash( apk, ByteBuffer.wrap(vSigner.verityRootHash), signatureInfo); } } diff --git a/core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java b/core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java index 939522dcd57f..1471870bd7d2 100644 --- a/core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java +++ b/core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java @@ -534,7 +534,7 @@ public class ApkSignatureSchemeV3Verifier { NoSuchAlgorithmException { try (RandomAccessFile apk = new RandomAccessFile(apkPath, "r")) { SignatureInfo signatureInfo = findSignature(apk); - return ApkVerityBuilder.generateApkVerity(apkPath, bufferFactory, signatureInfo); + return VerityBuilder.generateApkVerity(apkPath, bufferFactory, signatureInfo); } } @@ -547,7 +547,7 @@ public class ApkSignatureSchemeV3Verifier { if (vSigner.verityRootHash == null) { return null; } - return ApkVerityBuilder.generateApkVerityRootHash( + return VerityBuilder.generateApkVerityRootHash( apk, ByteBuffer.wrap(vSigner.verityRootHash), signatureInfo); } } diff --git a/core/java/android/util/apk/ApkSigningBlockUtils.java b/core/java/android/util/apk/ApkSigningBlockUtils.java index 081033ae84e9..87af5364c945 100644 --- a/core/java/android/util/apk/ApkSigningBlockUtils.java +++ b/core/java/android/util/apk/ApkSigningBlockUtils.java @@ -332,7 +332,7 @@ final class ApkSigningBlockUtils { try { byte[] expectedRootHash = parseVerityDigestAndVerifySourceLength(expectedDigest, apk.length(), signatureInfo); - ApkVerityBuilder.ApkVerityResult verity = ApkVerityBuilder.generateApkVerityTree(apk, + VerityBuilder.VerityResult verity = VerityBuilder.generateApkVerityTree(apk, signatureInfo, new ByteBufferFactory() { @Override public ByteBuffer create(int capacity) { diff --git a/core/java/android/util/apk/ApkVerityBuilder.java b/core/java/android/util/apk/VerityBuilder.java index edd09f8f73c4..443bbd8597af 100644 --- a/core/java/android/util/apk/ApkVerityBuilder.java +++ b/core/java/android/util/apk/VerityBuilder.java @@ -29,19 +29,18 @@ import java.security.NoSuchAlgorithmException; import java.util.ArrayList; /** - * ApkVerityBuilder builds the APK verity tree and the verity header. The generated tree format can - * be stored on disk for apk-verity setup and used by kernel. Note that since the current - * implementation is different from the upstream, we call this implementation apk-verity instead of - * fs-verity. + * VerityBuilder builds the verity Merkle tree and other metadata. The generated tree format can + * be stored on disk for fs-verity setup and used by kernel. The builder support standard + * fs-verity, and Android specific apk-verity that requires additional kernel patches. * - * <p>Unlike a regular Merkle tree, APK verity tree does not cover the content fully. Due to - * the existing APK format, it has to skip APK Signing Block and also has some special treatment for - * the "Central Directory offset" field of ZIP End of Central Directory. + * <p>Unlike a regular Merkle tree of fs-verity, the apk-verity tree does not cover the file content + * fully, and has to skip APK Signing Block with some special treatment for the "Central Directory + * offset" field of ZIP End of Central Directory. * * @hide */ -public abstract class ApkVerityBuilder { - private ApkVerityBuilder() {} +public abstract class VerityBuilder { + private VerityBuilder() {} private static final int CHUNK_SIZE_BYTES = 4096; // Typical Linux block size private static final int DIGEST_SIZE_BYTES = 32; // SHA-256 size @@ -52,7 +51,7 @@ public abstract class ApkVerityBuilder { private static final byte[] DEFAULT_SALT = new byte[8]; /** Result generated by the builder. */ - public static class ApkVerityResult { + public static class VerityResult { /** Raw fs-verity metadata and Merkle tree ready to be deployed on disk. */ public final ByteBuffer verityData; @@ -62,7 +61,7 @@ public abstract class ApkVerityBuilder { /** Root hash of the Merkle tree. */ public final byte[] rootHash; - private ApkVerityResult(ByteBuffer verityData, int merkleTreeSize, byte[] rootHash) { + private VerityResult(ByteBuffer verityData, int merkleTreeSize, byte[] rootHash) { this.verityData = verityData; this.merkleTreeSize = merkleTreeSize; this.rootHash = rootHash; @@ -74,14 +73,14 @@ public abstract class ApkVerityBuilder { * ByteBuffer} created by the {@link ByteBufferFactory}. The output is suitable to be used as * the on-disk format for fs-verity to use. * - * @return ApkVerityResult containing a buffer with the generated Merkle tree stored at the + * @return VerityResult containing a buffer with the generated Merkle tree stored at the * front, the tree size, and the calculated root hash. */ @NonNull - public static ApkVerityResult generateFsVerityTree(@NonNull RandomAccessFile apk, + public static VerityResult generateFsVerityTree(@NonNull RandomAccessFile apk, @NonNull ByteBufferFactory bufferFactory) throws IOException, SecurityException, NoSuchAlgorithmException, DigestException { - return generateVerityTree(apk, bufferFactory, null /* signatureInfo */, + return generateVerityTreeInternal(apk, bufferFactory, null /* signatureInfo */, false /* skipSigningBlock */); } @@ -91,18 +90,19 @@ public abstract class ApkVerityBuilder { * Block specificed in {@code signatureInfo}. The output is suitable to be used as the on-disk * format for fs-verity to use (with elide and patch extensions). * - * @return ApkVerityResult containing a buffer with the generated Merkle tree stored at the + * @return VerityResult containing a buffer with the generated Merkle tree stored at the * front, the tree size, and the calculated root hash. */ @NonNull - public static ApkVerityResult generateApkVerityTree(@NonNull RandomAccessFile apk, + public static VerityResult generateApkVerityTree(@NonNull RandomAccessFile apk, @Nullable SignatureInfo signatureInfo, @NonNull ByteBufferFactory bufferFactory) throws IOException, SecurityException, NoSuchAlgorithmException, DigestException { - return generateVerityTree(apk, bufferFactory, signatureInfo, true /* skipSigningBlock */); + return generateVerityTreeInternal(apk, bufferFactory, signatureInfo, + true /* skipSigningBlock */); } @NonNull - private static ApkVerityResult generateVerityTree(@NonNull RandomAccessFile apk, + private static VerityResult generateVerityTreeInternal(@NonNull RandomAccessFile apk, @NonNull ByteBufferFactory bufferFactory, @Nullable SignatureInfo signatureInfo, boolean skipSigningBlock) throws IOException, SecurityException, NoSuchAlgorithmException, DigestException { @@ -124,7 +124,7 @@ public abstract class ApkVerityBuilder { byte[] salt = skipSigningBlock ? DEFAULT_SALT : null; byte[] apkRootHash = generateVerityTreeInternal(apk, signatureInfo, salt, levelOffset, tree, skipSigningBlock); - return new ApkVerityResult(output, merkleTreeSize, apkRootHash); + return new VerityResult(output, merkleTreeSize, apkRootHash); } static void generateApkVerityFooter(@NonNull RandomAccessFile apk, @@ -173,7 +173,7 @@ public abstract class ApkVerityBuilder { throws IOException, SignatureNotFoundException, SecurityException, DigestException, NoSuchAlgorithmException { try (RandomAccessFile apk = new RandomAccessFile(apkPath, "r")) { - ApkVerityResult result = generateVerityTree(apk, bufferFactory, signatureInfo, + VerityResult result = generateVerityTreeInternal(apk, bufferFactory, signatureInfo, true /* skipSigningBlock */); ByteBuffer footer = slice(result.verityData, result.merkleTreeSize, result.verityData.limit()); diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 78e6dd8fb139..b2944d6a9923 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -15636,7 +15636,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, /** * Sets the visual z position of this view, in pixels. This is equivalent to setting the * {@link #setTranslationZ(float) translationZ} property to be the difference between - * the x value passed in and the current {@link #getElevation() elevation} property. + * the z value passed in and the current {@link #getElevation() elevation} property. * * @param z The visual z position of this view, in pixels. */ diff --git a/core/java/android/view/animation/Animation.java b/core/java/android/view/animation/Animation.java index 87b7b057e668..0e1f7675c8c3 100644 --- a/core/java/android/view/animation/Animation.java +++ b/core/java/android/view/animation/Animation.java @@ -203,11 +203,6 @@ public abstract class Animation implements Cloneable { */ private float mScaleFactor = 1f; - /** - * Don't animate the wallpaper. - */ - private boolean mDetachWallpaper = false; - private boolean mShowWallpaper; private boolean mMore = true; @@ -667,9 +662,10 @@ public abstract class Animation implements Cloneable { * * @param detachWallpaper true if the wallpaper should be detached from the animation * @attr ref android.R.styleable#Animation_detachWallpaper + * + * @deprecated All window animations are running with detached wallpaper. */ public void setDetachWallpaper(boolean detachWallpaper) { - mDetachWallpaper = detachWallpaper; } /** @@ -793,9 +789,11 @@ public abstract class Animation implements Cloneable { /** * Return value of {@link #setDetachWallpaper(boolean)}. * @attr ref android.R.styleable#Animation_detachWallpaper + * + * @deprecated All window animations are running with detached wallpaper. */ public boolean getDetachWallpaper() { - return mDetachWallpaper; + return false; } /** diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java index 7abe19e795f4..d4c7069cdbf4 100644 --- a/core/java/android/view/autofill/AutofillManager.java +++ b/core/java/android/view/autofill/AutofillManager.java @@ -803,10 +803,6 @@ public final class AutofillManager { return true; } } - if (sVerbose) { - Log.v(TAG, "not ignoring notifyViewEntered(flags=" + flags + ", view=" + id - + ", state " + getStateAsStringLocked() + ", enteredIds=" + mEnteredIds); - } return false; } @@ -845,6 +841,9 @@ public final class AutofillManager { ensureServiceClientAddedIfNeededLocked(); if (!mEnabled) { + if (sVerbose) { + Log.v(TAG, "ignoring notifyViewEntered(" + id + "): disabled"); + } if (mCallback != null) { callback = mCallback; } @@ -995,6 +994,9 @@ public final class AutofillManager { ensureServiceClientAddedIfNeededLocked(); if (!mEnabled) { + if (sVerbose) { + Log.v(TAG, "ignoring notifyViewEntered(" + id + "): disabled"); + } if (mCallback != null) { callback = mCallback; } diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index f95b3ce98264..9d06680c4c07 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -882,9 +882,6 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener private boolean mTextSetFromXmlOrResourceId = false; // Resource id used to set the text. private @StringRes int mTextId = ResourceId.ID_NULL; - // Last value used on AFM.notifyValueChanged(), used to optimize autofill workflow by avoiding - // calls when the value did not change - private CharSequence mLastValueSentToAutofillManager; // // End of autofill-related attributes @@ -5884,7 +5881,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener if (needEditableForNotification) { sendAfterTextChanged((Editable) text); } else { - notifyAutoFillManagerAfterTextChangedIfNeeded(); + notifyAutoFillManagerAfterTextChanged(); } // SelectionModifierCursorController depends on textCanBeSelected, which depends on text @@ -9933,33 +9930,23 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } // Always notify AutoFillManager - it will return right away if autofill is disabled. - notifyAutoFillManagerAfterTextChangedIfNeeded(); + notifyAutoFillManagerAfterTextChanged(); hideErrorIfUnchanged(); } - private void notifyAutoFillManagerAfterTextChangedIfNeeded() { + private void notifyAutoFillManagerAfterTextChanged() { // It is important to not check whether the view is important for autofill // since the user can trigger autofill manually on not important views. if (!isAutofillable()) { return; } final AutofillManager afm = mContext.getSystemService(AutofillManager.class); - if (afm == null) { - return; - } - - if (mLastValueSentToAutofillManager == null - || !mLastValueSentToAutofillManager.equals(mText)) { + if (afm != null) { if (android.view.autofill.Helper.sVerbose) { - Log.v(LOG_TAG, "notifying AFM after text changed"); + Log.v(LOG_TAG, "notifyAutoFillManagerAfterTextChanged"); } afm.notifyValueChanged(TextView.this); - mLastValueSentToAutofillManager = mText; - } else { - if (android.view.autofill.Helper.sVerbose) { - Log.v(LOG_TAG, "not notifying AFM on unchanged text"); - } } } diff --git a/core/java/com/android/internal/hardware/AmbientDisplayConfiguration.java b/core/java/com/android/internal/hardware/AmbientDisplayConfiguration.java index cb282b69845c..0080ace230a2 100644 --- a/core/java/com/android/internal/hardware/AmbientDisplayConfiguration.java +++ b/core/java/com/android/internal/hardware/AmbientDisplayConfiguration.java @@ -66,13 +66,13 @@ public class AmbientDisplayConfiguration { return !TextUtils.isEmpty(doubleTapSensorType()); } - public boolean reachGestureEnabled(int user) { - return boolSettingDefaultOn(Settings.Secure.DOZE_REACH_GESTURE, user) - && reachGestureAvailable(); + public boolean wakeLockScreenGestureEnabled(int user) { + return boolSettingDefaultOn(Settings.Secure.DOZE_WAKE_LOCK_SCREEN_GESTURE, user) + && wakeLockScreenGestureAvailable(); } - public boolean reachGestureAvailable() { - return !TextUtils.isEmpty(reachSensorType()); + public boolean wakeLockScreenGestureAvailable() { + return !TextUtils.isEmpty(wakeLockScreenSensorType()); } public boolean wakeScreenGestureEnabled(int user) { @@ -92,8 +92,8 @@ public class AmbientDisplayConfiguration { return mContext.getResources().getString(R.string.config_dozeLongPressSensorType); } - public String reachSensorType() { - return mContext.getResources().getString(R.string.config_dozeReachSensorType); + public String wakeLockScreenSensorType() { + return mContext.getResources().getString(R.string.config_dozeWakeLockScreenSensorType); } public String wakeScreenSensorType() { diff --git a/core/java/com/android/internal/os/BinderCallsStats.java b/core/java/com/android/internal/os/BinderCallsStats.java index c0c358d9b44b..856712f48297 100644 --- a/core/java/com/android/internal/os/BinderCallsStats.java +++ b/core/java/com/android/internal/os/BinderCallsStats.java @@ -436,6 +436,12 @@ public class BinderCallsStats implements BinderInternal.Observer { } public void setSamplingInterval(int samplingInterval) { + if (samplingInterval <= 0) { + Slog.w(TAG, "Ignored invalid sampling interval (value must be positive): " + + samplingInterval); + return; + } + synchronized (mLock) { if (samplingInterval != mPeriodicSamplingInterval) { mPeriodicSamplingInterval = samplingInterval; diff --git a/core/java/com/android/internal/os/StoragedUidIoStatsReader.java b/core/java/com/android/internal/os/StoragedUidIoStatsReader.java new file mode 100644 index 000000000000..9b0346923cd3 --- /dev/null +++ b/core/java/com/android/internal/os/StoragedUidIoStatsReader.java @@ -0,0 +1,113 @@ +/* + * 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.internal.os; + +import android.os.StrictMode; +import android.text.TextUtils; +import android.util.Slog; + +import com.android.internal.annotations.VisibleForTesting; + +import java.io.BufferedReader; +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; + + +/** + * Reads /proc/uid_io/stats which has the line format: + * + * uid: foreground_read_chars foreground_write_chars foreground_read_bytes foreground_write_bytes + * background_read_chars background_write_chars background_read_bytes background_write_bytes + * foreground_fsync background_fsync + * + * This provides the number of bytes/chars read/written in foreground/background for each uid. + * The file contains a monotonically increasing count of bytes/chars for a single boot. + */ +public class StoragedUidIoStatsReader { + + private static final String TAG = StoragedUidIoStatsReader.class.getSimpleName(); + private static String sUidIoFile = "/proc/uid_io/stats"; + + public StoragedUidIoStatsReader() { + } + + @VisibleForTesting + public StoragedUidIoStatsReader(String file) { + sUidIoFile = file; + } + + /** + * Notifies when new data is available. + */ + public interface Callback { + + /** + * Provides data to the client. + * + * Note: Bytes are I/O events from a storage device. Chars are data requested by syscalls, + * and can be satisfied by caching. + */ + void onUidStorageStats(int uid, long fgCharsRead, long fgCharsWrite, long fgBytesRead, + long fgBytesWrite, long bgCharsRead, long bgCharsWrite, long bgBytesRead, + long bgBytesWrite, long fgFsync, long bgFsync); + } + + /** + * Reads the proc file, calling into the callback with raw absolute value of I/O stats + * for each UID. + * + * @param callback The callback to invoke for each line of the proc file. + */ + public void readAbsolute(Callback callback) { + final int oldMask = StrictMode.allowThreadDiskReadsMask(); + File file = new File(sUidIoFile); + try (BufferedReader reader = Files.newBufferedReader(file.toPath())) { + String line; + while ((line = reader.readLine()) != null) { + String[] fields = TextUtils.split(line, " "); + if (fields.length != 11) { + Slog.e(TAG, "Malformed entry in " + sUidIoFile + ": " + line); + continue; + } + try { + final String uidStr = fields[0]; + final int uid = Integer.parseInt(fields[0], 10); + final long fgCharsRead = Long.parseLong(fields[1], 10); + final long fgCharsWrite = Long.parseLong(fields[2], 10); + final long fgBytesRead = Long.parseLong(fields[3], 10); + final long fgBytesWrite = Long.parseLong(fields[4], 10); + final long bgCharsRead = Long.parseLong(fields[5], 10); + final long bgCharsWrite = Long.parseLong(fields[6], 10); + final long bgBytesRead = Long.parseLong(fields[7], 10); + final long bgBytesWrite = Long.parseLong(fields[8], 10); + final long fgFsync = Long.parseLong(fields[9], 10); + final long bgFsync = Long.parseLong(fields[10], 10); + callback.onUidStorageStats(uid, fgCharsRead, fgCharsWrite, fgBytesRead, + fgBytesWrite, bgCharsRead, bgCharsWrite, bgBytesRead, bgBytesWrite, + fgFsync, bgFsync); + } catch (NumberFormatException e) { + Slog.e(TAG, "Could not parse entry in " + sUidIoFile + ": " + e.getMessage()); + } + } + } catch (IOException e) { + Slog.e(TAG, "Failed to read " + sUidIoFile + ": " + e.getMessage()); + } finally { + StrictMode.setThreadPolicyMask(oldMask); + } + } +} diff --git a/core/jni/android_hardware_display_DisplayViewport.cpp b/core/jni/android_hardware_display_DisplayViewport.cpp index ab8e685d5441..05f6556bfb35 100644 --- a/core/jni/android_hardware_display_DisplayViewport.cpp +++ b/core/jni/android_hardware_display_DisplayViewport.cpp @@ -40,6 +40,7 @@ static struct { jfieldID deviceWidth; jfieldID deviceHeight; jfieldID uniqueId; + jfieldID type; } gDisplayViewportClassInfo; static struct { @@ -64,6 +65,9 @@ status_t android_hardware_display_DisplayViewport_toNative(JNIEnv* env, jobject viewport->uniqueId = ScopedUtfChars(env, uniqueId).c_str(); } + viewport->type = static_cast<ViewportType>(env->GetIntField(viewportObj, + gDisplayViewportClassInfo.type)); + jobject logicalFrameObj = env->GetObjectField(viewportObj, gDisplayViewportClassInfo.logicalFrame); viewport->logicalLeft = env->GetIntField(logicalFrameObj, gRectClassInfo.left); @@ -108,6 +112,9 @@ int register_android_hardware_display_DisplayViewport(JNIEnv* env) { gDisplayViewportClassInfo.uniqueId = GetFieldIDOrDie(env, gDisplayViewportClassInfo.clazz, "uniqueId", "Ljava/lang/String;"); + gDisplayViewportClassInfo.type = GetFieldIDOrDie(env, + gDisplayViewportClassInfo.clazz, "type", "I"); + clazz = FindClassOrDie(env, "android/graphics/Rect"); gRectClassInfo.left = GetFieldIDOrDie(env, clazz, "left", "I"); gRectClassInfo.top = GetFieldIDOrDie(env, clazz, "top", "I"); diff --git a/core/jni/android_net_LocalSocketImpl.cpp b/core/jni/android_net_LocalSocketImpl.cpp index 6df23f72bdd3..a1f2377041e8 100644 --- a/core/jni/android_net_LocalSocketImpl.cpp +++ b/core/jni/android_net_LocalSocketImpl.cpp @@ -58,6 +58,11 @@ socket_connect_local(JNIEnv *env, jobject object, int ret; int fd; + if (name == NULL) { + jniThrowNullPointerException(env, NULL); + return; + } + fd = jniGetFDFromFileDescriptor(env, fileDescriptor); if (env->ExceptionCheck()) { diff --git a/core/jni/android_os_GraphicsEnvironment.cpp b/core/jni/android_os_GraphicsEnvironment.cpp index e3bec3cb29c9..b70485d9a0ea 100644 --- a/core/jni/android_os_GraphicsEnvironment.cpp +++ b/core/jni/android_os_GraphicsEnvironment.cpp @@ -28,9 +28,12 @@ void setDriverPath(JNIEnv* env, jobject clazz, jstring path) { android::GraphicsEnv::getInstance().setDriverPath(pathChars.c_str()); } -void setAnglePath(JNIEnv* env, jobject clazz, jstring path) { +void setAngleInfo_native(JNIEnv* env, jobject clazz, jstring path, jstring appName, jstring appPref, jboolean devOptIn) { ScopedUtfChars pathChars(env, path); - android::GraphicsEnv::getInstance().setAnglePath(pathChars.c_str()); + ScopedUtfChars appNameChars(env, appName); + ScopedUtfChars appPrefChars(env, appPref); + android::GraphicsEnv::getInstance().setAngleInfo(pathChars.c_str(), appNameChars.c_str(), + appPrefChars.c_str(), devOptIn); } void setLayerPaths_native(JNIEnv* env, jobject clazz, jobject classLoader, jstring layerPaths) { @@ -49,7 +52,7 @@ void setDebugLayers_native(JNIEnv* env, jobject clazz, jstring layers) { const JNINativeMethod g_methods[] = { { "setDriverPath", "(Ljava/lang/String;)V", reinterpret_cast<void*>(setDriverPath) }, - { "setAnglePath", "(Ljava/lang/String;)V", reinterpret_cast<void*>(setAnglePath) }, + { "setAngleInfo", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Z)V", reinterpret_cast<void*>(setAngleInfo_native) }, { "setLayerPaths", "(Ljava/lang/ClassLoader;Ljava/lang/String;)V", reinterpret_cast<void*>(setLayerPaths_native) }, { "setDebugLayers", "(Ljava/lang/String;)V", reinterpret_cast<void*>(setDebugLayers_native) }, }; diff --git a/core/proto/android/providers/settings/global.proto b/core/proto/android/providers/settings/global.proto index f9f725a130ed..14ed9e6eeadb 100644 --- a/core/proto/android/providers/settings/global.proto +++ b/core/proto/android/providers/settings/global.proto @@ -397,6 +397,9 @@ message GlobalSettingsProto { // Ordered GPU debug layer list // i.e. <layer1>:<layer2>:...:<layerN> optional SettingProto debug_layers = 2 [ (android.privacy).dest = DEST_AUTOMATIC ]; + + // App will load ANGLE instead of native GLES drivers. + optional SettingProto angle_enabled_app = 3; } optional Gpu gpu = 59; diff --git a/core/proto/android/server/usagestatsservice.proto b/core/proto/android/server/usagestatsservice.proto new file mode 100644 index 000000000000..941c81fbb8df --- /dev/null +++ b/core/proto/android/server/usagestatsservice.proto @@ -0,0 +1,100 @@ +/* + * 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. + */ + +syntax = "proto2"; +package com.android.server.usage; +import "frameworks/base/core/proto/android/content/configuration.proto"; +import "frameworks/base/libs/incident/proto/android/privacy.proto"; + +option java_multiple_files = true; + +message IntervalStatsProto { + message StringPool { + optional int32 size = 1; + repeated string strings = 2; + } + + message CountAndTime { + optional int32 count = 1; + optional int64 time_ms = 2; + } + + // Stores the relevant information from a UsageStats + message UsageStats { + message ChooserAction { + message CategoryCount { + optional string name = 1; + optional int32 count = 3; + } + optional string name = 1; + repeated CategoryCount counts = 3; + } + optional string package = 1; + // package_index contains the index + 1 of the package name in the string pool + optional int32 package_index = 2; + optional int64 last_time_active_ms = 3; + optional int64 total_time_active_ms = 4; + optional int32 last_event = 5; + optional int32 app_launch_count = 6; + repeated ChooserAction chooser_actions = 7; + } + + // Stores the relevant information an IntervalStats will have about a Configuration + message Configuration { + optional .android.content.ConfigurationProto config = 1; + optional int64 last_time_active_ms = 2; + optional int64 total_time_active_ms = 3; + optional int32 count = 4; + optional bool active = 5; + } + + // Stores the relevant information from a UsageEvents.Event + message Event { + optional string package = 1; + // package_index contains the index + 1 of the package name in the string pool + optional int32 package_index = 2; + optional string class = 3; + // class_index contains the index + 1 of the class name in the string pool + optional int32 class_index = 4; + optional int64 time_ms = 5; + optional int32 flags = 6; + optional int32 type = 7; + optional .android.content.ConfigurationProto config = 8; + optional string shortcut_id = 9; + optional int32 standby_bucket = 11; + optional string notification_channel = 12; + // notification_channel_index contains the index + 1 of the channel name in the string pool + optional int32 notification_channel_index = 13; + } + + // The following fields contain supplemental data used to build IntervalStats, such as a string + // pool. + optional int64 end_time_ms = 1; + // stringpool contains all the package and class names used by UsageStats and Event + // They will hold a number that is equal to the index + 1 of their string in the pool + optional StringPool stringpool = 2; + + // The following fields contain aggregated usage stats data + optional CountAndTime interactive = 10; + optional CountAndTime non_interactive = 11; + optional CountAndTime keyguard_shown = 12; + optional CountAndTime keyguard_hidden = 13; + + // The following fields contain listed usage stats data + repeated UsageStats packages = 20; + repeated Configuration configurations = 21; + repeated Event event_log = 22; +} diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 85a52d5f714f..f654ce231161 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -941,7 +941,6 @@ Requesting this by itself is not sufficient to give you location access. <p>Protection level: dangerous - @hide --> <permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" android:permissionGroup="android.permission-group.LOCATION" @@ -1186,9 +1185,9 @@ android:priority="700" /> <!-- Required to be able to access the camera device. - <p>This will automatically enforce the <a - href="{@docRoot}guide/topics/manifest/uses-feature-element.html"> - <uses-feature>}</a> manifest element for <em>all</em> camera features. + <p>This will automatically enforce the + <a href="{@docRoot}guide/topics/manifest/uses-feature-element.html"> + uses-feature</a> manifest element for <em>all</em> camera features. If you do not require all camera features or can properly operate if a camera is not available, then you must modify your manifest as appropriate in order to install on devices that don't support all camera features.</p> @@ -3407,6 +3406,12 @@ <permission android:name="android.permission.DEVICE_POWER" android:protectionLevel="signature" /> + <!-- Allows toggling battery saver on the system. + Superseded by DEVICE_POWER permission. @hide @SystemApi + --> + <permission android:name="android.permission.POWER_SAVER" + android:protectionLevel="signature|privileged" /> + <!-- Allows access to the PowerManager.userActivity function. <p>Not for use by third-party applications. @hide @SystemApi --> <permission android:name="android.permission.USER_ACTIVITY" @@ -4148,6 +4153,11 @@ <permission android:name="android.permission.BIND_SMS_APP_SERVICE" android:protectionLevel="signature" /> + <!-- @hide Permission that allows background clipboard access. + <p>Not for use by third-party applications. --> + <permission android:name="android.permission.READ_CLIPBOARD_IN_BACKGROUND" + android:protectionLevel="signature" /> + <application android:process="system" android:persistent="true" android:hasCode="false" diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml index 6ae25417e386..bebd4896e89b 100644 --- a/core/res/res/values-hi/strings.xml +++ b/core/res/res/values-hi/strings.xml @@ -531,7 +531,7 @@ <skip /> <string name="fingerprint_authenticated" msgid="5309333983002526448">"फ़िंगरप्रिंट की पुष्टि हो गई"</string> <string name="face_authenticated_no_confirmation_required" msgid="4018680978348659031">"चेहरे की पहचान की गई"</string> - <string name="face_authenticated_confirmation_required" msgid="8778347003507633610">"चेहरा की पहचान की गई, कृपया पुष्टि बटन दबाएं"</string> + <string name="face_authenticated_confirmation_required" msgid="8778347003507633610">"चेहरे की पहचान की गई, कृपया पुष्टि बटन दबाएं"</string> <string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"फ़िंगरप्रिंट हार्डवेयर उपलब्ध नहीं है."</string> <string name="fingerprint_error_no_space" msgid="1055819001126053318">"फ़िंगरप्रिंट को संग्रहित नहीं किया जा सका. कृपया कोई मौजूदा फ़िंगरप्रिंट निकालें."</string> <string name="fingerprint_error_timeout" msgid="3927186043737732875">"फ़िंगरप्रिंट का समय समाप्त हो गया. पुनः प्रयास करें."</string> diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml index 41616df4209d..912db2001851 100644 --- a/core/res/res/values-tr/strings.xml +++ b/core/res/res/values-tr/strings.xml @@ -529,7 +529,7 @@ <string name="biometric_not_recognized" msgid="5770511773560736082">"Tanınmadı"</string> <string name="fingerprint_authenticated" msgid="5309333983002526448">"Parmak izi kimlik doğrulaması yapıldı"</string> <string name="face_authenticated_no_confirmation_required" msgid="4018680978348659031">"Yüz kimliği doğrulandı"</string> - <string name="face_authenticated_confirmation_required" msgid="8778347003507633610">"Yüz kimliği doğrulandı, lütfen doğrula\'ya basın"</string> + <string name="face_authenticated_confirmation_required" msgid="8778347003507633610">"Yüz kimliği doğrulandı, lütfen onayla\'ya basın"</string> <string name="fingerprint_error_hw_not_available" msgid="7955921658939936596">"Parmak izi donanımı kullanılamıyor."</string> <string name="fingerprint_error_no_space" msgid="1055819001126053318">"Parmak izi depolanamıyor. Lütfen mevcut parmak izlerinden birini kaldırın."</string> <string name="fingerprint_error_timeout" msgid="3927186043737732875">"Parmak izi için zaman aşımı oluştu. Tekrar deneyin."</string> diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml index 65b88076abf0..cdb65edd81e7 100644 --- a/core/res/res/values/attrs.xml +++ b/core/res/res/values/attrs.xml @@ -1425,7 +1425,7 @@ at {@link android.view.inputmethod.InputConnection#performEditorAction(int) InputConnection.performEditorAction(int)}. <p>Corresponds to - {@link android.view.inputmethod.EditorInfo#IME_FLAG_NO_FULLSCREEN}. --> + {@link android.view.inputmethod.EditorInfo#IME_FLAG_NAVIGATE_PREVIOUS}. --> <flag name="flagNavigatePrevious" value="0x4000000" /> <!-- Used to specify that there is something interesting that a forward navigation can focus on. This is like using @@ -4184,9 +4184,9 @@ row is full. The rowCount attribute may be used similarly in the vertical case. The default is horizontal. --> <attr name="orientation" /> - <!-- The maxmimum number of rows to create when automatically positioning children. --> + <!-- The maximum number of rows to create when automatically positioning children. --> <attr name="rowCount" format="integer" /> - <!-- The maxmimum number of columns to create when automatically positioning children. --> + <!-- The maximum number of columns to create when automatically positioning children. --> <attr name="columnCount" format="integer" /> <!-- When set to true, tells GridLayout to use default margins when none are specified in a view's layout parameters. diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml index 3c0e51ed4cdd..8ff29ba4614c 100644 --- a/core/res/res/values/attrs_manifest.xml +++ b/core/res/res/values/attrs_manifest.xml @@ -1102,6 +1102,10 @@ <p>The default value of this attribute is <code>false</code>. --> <attr name="isFeatureSplit" format="boolean" /> + <!-- Flag to specify if this APK requires at least one split [either feature or + resource] to be present in order to function. Default value is false. --> + <attr name="isSplitRequired" format="boolean" /> + <!-- Extra options for an activity's UI. Applies to either the {@code <activity>} or {@code <application>} tag. If specified on the {@code <application>} tag these will be considered defaults for all activities in the @@ -1422,6 +1426,7 @@ <attr name="targetSandboxVersion" /> <attr name="compileSdkVersion" /> <attr name="compileSdkVersionCodename" /> + <attr name="isSplitRequired" /> </declare-styleable> <!-- The <code>application</code> tag describes application-level components diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index 9aebf6c4597f..0daf5a76562d 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -2113,8 +2113,8 @@ <!-- Type of the long press sensor. Empty if long press is not supported. --> <string name="config_dozeLongPressSensorType" translatable="false"></string> - <!-- Type of the reach sensor. Empty if reach is not supported. --> - <string name="config_dozeReachSensorType" translatable="false"></string> + <!-- Type of sensor that wakes up the lock screen. Empty if not supported. --> + <string name="config_dozeWakeLockScreenSensorType" translatable="false"></string> <!-- Type of the wake up sensor. Empty if not supported. --> <string name="config_dozeWakeScreenSensorType" translatable="false"></string> diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml index cdaff18cf38a..fadefff6a030 100644 --- a/core/res/res/values/public.xml +++ b/core/res/res/values/public.xml @@ -2913,6 +2913,7 @@ <public name="usesNonSdkApi" /> <public name="minimumUiTimeout" /> <public name="isLightTheme" /> + <public name="isSplitRequired" /> </public-group> <public-group type="drawable" first-id="0x010800b4"> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 9f2256a6461a..72ae0d61654a 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -3279,7 +3279,7 @@ <java-symbol type="array" name="config_hideWhenDisabled_packageNames" /> <java-symbol type="string" name="config_dozeLongPressSensorType" /> - <java-symbol type="string" name="config_dozeReachSensorType" /> + <java-symbol type="string" name="config_dozeWakeLockScreenSensorType" /> <java-symbol type="array" name="config_allowedGlobalInstantAppSettings" /> <java-symbol type="array" name="config_allowedSystemInstantAppSettings" /> diff --git a/core/tests/coretests/src/android/graphics/TypefaceSystemFallbackTest.java b/core/tests/coretests/src/android/graphics/TypefaceSystemFallbackTest.java index 3ce258969822..6fdb71fcbab0 100644 --- a/core/tests/coretests/src/android/graphics/TypefaceSystemFallbackTest.java +++ b/core/tests/coretests/src/android/graphics/TypefaceSystemFallbackTest.java @@ -24,6 +24,7 @@ import static org.junit.Assert.assertTrue; import android.content.Context; import android.content.res.AssetManager; import android.graphics.fonts.Font; +import android.graphics.fonts.FontCustomizationParser; import android.graphics.fonts.FontFamily; import android.graphics.fonts.SystemFonts; import android.support.test.InstrumentationRegistry; @@ -36,12 +37,15 @@ import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.xmlpull.v1.XmlPullParserException; +import java.io.ByteArrayInputStream; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.StandardCopyOption; import java.util.ArrayList; @@ -62,6 +66,8 @@ public class TypefaceSystemFallbackTest { }; private static final String TEST_FONTS_XML; private static final String TEST_FONT_DIR; + private static final String TEST_OEM_XML; + private static final String TEST_OEM_DIR; private static final float GLYPH_1EM_WIDTH; private static final float GLYPH_2EM_WIDTH; @@ -73,8 +79,13 @@ public class TypefaceSystemFallbackTest { if (!cacheDir.isDirectory()) { cacheDir.mkdirs(); } - TEST_FONT_DIR = cacheDir.getAbsolutePath() + "/"; + TEST_FONT_DIR = cacheDir.getAbsolutePath() + "/fonts/"; TEST_FONTS_XML = new File(cacheDir, "fonts.xml").getAbsolutePath(); + TEST_OEM_DIR = cacheDir.getAbsolutePath() + "/oem_fonts/"; + TEST_OEM_XML = new File(cacheDir, "fonts_customization.xml").getAbsolutePath(); + + new File(TEST_FONT_DIR).mkdirs(); + new File(TEST_OEM_DIR).mkdirs(); final AssetManager am = InstrumentationRegistry.getInstrumentation().getContext().getAssets(); @@ -99,6 +110,12 @@ public class TypefaceSystemFallbackTest { } catch (IOException e) { throw new RuntimeException(e); } + final File outOemInCache = new File(TEST_OEM_DIR, fontFile); + try (InputStream is = am.open(sourceInAsset)) { + Files.copy(is, outOemInCache.toPath(), StandardCopyOption.REPLACE_EXISTING); + } catch (IOException e) { + throw new RuntimeException(e); + } } } @@ -107,11 +124,14 @@ public class TypefaceSystemFallbackTest { for (final String fontFile : TEST_FONT_FILES) { final File outInCache = new File(TEST_FONT_DIR, fontFile); outInCache.delete(); + final File outOemInCache = new File(TEST_OEM_DIR, fontFile); + outInCache.delete(); } } private static void buildSystemFallback(String xml, - ArrayMap<String, Typeface> fontMap, ArrayMap<String, FontFamily[]> fallbackMap) { + FontCustomizationParser.Result oemCustomization, ArrayMap<String, Typeface> fontMap, + ArrayMap<String, FontFamily[]> fallbackMap) { final ArrayList<Font> availableFonts = new ArrayList<>(); try (FileOutputStream fos = new FileOutputStream(TEST_FONTS_XML)) { fos.write(xml.getBytes(Charset.forName("UTF-8"))); @@ -119,18 +139,28 @@ public class TypefaceSystemFallbackTest { throw new RuntimeException(e); } final FontConfig.Alias[] aliases = SystemFonts.buildSystemFallback(TEST_FONTS_XML, - TEST_FONT_DIR, fallbackMap, availableFonts); + TEST_FONT_DIR, oemCustomization, fallbackMap, availableFonts); Typeface.initSystemDefaultTypefaces(fontMap, fallbackMap, aliases); } + private static FontCustomizationParser.Result readFontCustomization(String oemXml) { + try (InputStream is = new ByteArrayInputStream(oemXml.getBytes(StandardCharsets.UTF_8))) { + return FontCustomizationParser.parse(is, TEST_OEM_DIR); + } catch (IOException | XmlPullParserException e) { + throw new RuntimeException(e); + } + } + @Test public void testBuildSystemFallback() { final ArrayMap<String, Typeface> fontMap = new ArrayMap<>(); final ArrayMap<String, FontFamily[]> fallbackMap = new ArrayMap<>(); final ArrayList<Font> availableFonts = new ArrayList<>(); + final FontCustomizationParser.Result oemCustomization = + new FontCustomizationParser.Result(); final FontConfig.Alias[] aliases = SystemFonts.buildSystemFallback(SYSTEM_FONTS_XML, - SYSTEM_FONT_DIR, fallbackMap, availableFonts); + SYSTEM_FONT_DIR, oemCustomization, fallbackMap, availableFonts); assertNotNull(aliases); assertFalse(fallbackMap.isEmpty()); @@ -156,8 +186,10 @@ public class TypefaceSystemFallbackTest { + "</familyset>"; final ArrayMap<String, Typeface> fontMap = new ArrayMap<>(); final ArrayMap<String, FontFamily[]> fallbackMap = new ArrayMap<>(); + final FontCustomizationParser.Result oemCustomization = + new FontCustomizationParser.Result(); - buildSystemFallback(xml, fontMap, fallbackMap); + buildSystemFallback(xml, oemCustomization, fontMap, fallbackMap); assertEquals(1, fontMap.size()); assertTrue(fontMap.containsKey("sans-serif")); @@ -184,8 +216,10 @@ public class TypefaceSystemFallbackTest { + "</familyset>"; final ArrayMap<String, Typeface> fontMap = new ArrayMap<>(); final ArrayMap<String, FontFamily[]> fallbackMap = new ArrayMap<>(); + final FontCustomizationParser.Result oemCustomization = + new FontCustomizationParser.Result(); - buildSystemFallback(xml, fontMap, fallbackMap); + buildSystemFallback(xml, oemCustomization, fontMap, fallbackMap); final Paint paint = new Paint(); @@ -230,8 +264,10 @@ public class TypefaceSystemFallbackTest { + "</familyset>"; final ArrayMap<String, Typeface> fontMap = new ArrayMap<>(); final ArrayMap<String, FontFamily[]> fallbackMap = new ArrayMap<>(); + final FontCustomizationParser.Result oemCustomization = + new FontCustomizationParser.Result(); - buildSystemFallback(xml, fontMap, fallbackMap); + buildSystemFallback(xml, oemCustomization, fontMap, fallbackMap); final Paint paint = new Paint(); @@ -275,8 +311,10 @@ public class TypefaceSystemFallbackTest { + "</familyset>"; final ArrayMap<String, Typeface> fontMap = new ArrayMap<>(); final ArrayMap<String, FontFamily[]> fallbackMap = new ArrayMap<>(); + final FontCustomizationParser.Result oemCustomization = + new FontCustomizationParser.Result(); - buildSystemFallback(xml, fontMap, fallbackMap); + buildSystemFallback(xml, oemCustomization, fontMap, fallbackMap); final Paint paint = new Paint(); @@ -325,8 +363,10 @@ public class TypefaceSystemFallbackTest { + "</familyset>"; final ArrayMap<String, Typeface> fontMap = new ArrayMap<>(); final ArrayMap<String, FontFamily[]> fallbackMap = new ArrayMap<>(); + final FontCustomizationParser.Result oemCustomization = + new FontCustomizationParser.Result(); - buildSystemFallback(xml, fontMap, fallbackMap); + buildSystemFallback(xml, oemCustomization, fontMap, fallbackMap); final Paint paint = new Paint(); @@ -371,8 +411,10 @@ public class TypefaceSystemFallbackTest { + "</familyset>"; final ArrayMap<String, Typeface> fontMap = new ArrayMap<>(); final ArrayMap<String, FontFamily[]> fallbackMap = new ArrayMap<>(); + final FontCustomizationParser.Result oemCustomization = + new FontCustomizationParser.Result(); - buildSystemFallback(xml, fontMap, fallbackMap); + buildSystemFallback(xml, oemCustomization, fontMap, fallbackMap); final Paint paint = new Paint(); @@ -410,8 +452,10 @@ public class TypefaceSystemFallbackTest { + "</familyset>"; final ArrayMap<String, Typeface> fontMap = new ArrayMap<>(); final ArrayMap<String, FontFamily[]> fallbackMap = new ArrayMap<>(); + final FontCustomizationParser.Result oemCustomization = + new FontCustomizationParser.Result(); - buildSystemFallback(xml, fontMap, fallbackMap); + buildSystemFallback(xml, oemCustomization, fontMap, fallbackMap); final Paint paint = new Paint(); @@ -449,8 +493,10 @@ public class TypefaceSystemFallbackTest { + "</familyset>"; final ArrayMap<String, Typeface> fontMap = new ArrayMap<>(); final ArrayMap<String, FontFamily[]> fallbackMap = new ArrayMap<>(); + final FontCustomizationParser.Result oemCustomization = + new FontCustomizationParser.Result(); - buildSystemFallback(xml, fontMap, fallbackMap); + buildSystemFallback(xml, oemCustomization, fontMap, fallbackMap); final Paint paint = new Paint(); @@ -497,8 +543,10 @@ public class TypefaceSystemFallbackTest { + "</familyset>"; final ArrayMap<String, Typeface> fontMap = new ArrayMap<>(); final ArrayMap<String, FontFamily[]> fallbackMap = new ArrayMap<>(); + final FontCustomizationParser.Result oemCustomization = + new FontCustomizationParser.Result(); - buildSystemFallback(xml, fontMap, fallbackMap); + buildSystemFallback(xml, oemCustomization, fontMap, fallbackMap); final Paint paint = new Paint(); paint.setTypeface(fontMap.get("sans-serif")); @@ -539,8 +587,10 @@ public class TypefaceSystemFallbackTest { + "</familyset>"; final ArrayMap<String, Typeface> fontMap = new ArrayMap<>(); final ArrayMap<String, FontFamily[]> fallbackMap = new ArrayMap<>(); + final FontCustomizationParser.Result oemCustomization = + new FontCustomizationParser.Result(); - buildSystemFallback(xml, fontMap, fallbackMap); + buildSystemFallback(xml, oemCustomization, fontMap, fallbackMap); final Paint paint = new Paint(); @@ -578,8 +628,10 @@ public class TypefaceSystemFallbackTest { + "</familyset>"; final ArrayMap<String, Typeface> fontMap = new ArrayMap<>(); final ArrayMap<String, FontFamily[]> fallbackMap = new ArrayMap<>(); + final FontCustomizationParser.Result oemCustomization = + new FontCustomizationParser.Result(); - buildSystemFallback(xml, fontMap, fallbackMap); + buildSystemFallback(xml, oemCustomization, fontMap, fallbackMap); final Paint paint = new Paint(); @@ -598,4 +650,191 @@ public class TypefaceSystemFallbackTest { assertEquals(GLYPH_1EM_WIDTH, paint.measureText("c"), 0.0f); } + @Test + public void testBuildSystemFallback__Customization_new_named_family() { + final String xml = "<?xml version='1.0' encoding='UTF-8'?>" + + "<familyset>" + + " <family name='sans-serif'>" + + " <font weight='400' style='normal'>a3em.ttf</font>" + + " </family>" + + "</familyset>"; + final String oemXml = "<?xml version='1.0' encoding='UTF-8'?>" + + "<fonts-modification version='1'>" + + " <family customizationType='new-named-family' name='google-sans'>" + + " <font weight='400' style='normal'>b3em.ttf</font>" + + " </family>" + + "</fonts-modification>"; + final ArrayMap<String, Typeface> fontMap = new ArrayMap<>(); + final ArrayMap<String, FontFamily[]> fallbackMap = new ArrayMap<>(); + final FontCustomizationParser.Result oemCustomization = + readFontCustomization(oemXml); + + buildSystemFallback(xml, oemCustomization, fontMap, fallbackMap); + + final Paint paint = new Paint(); + + Typeface testTypeface = fontMap.get("sans-serif"); + assertNotNull(testTypeface); + paint.setTypeface(testTypeface); + assertEquals(GLYPH_3EM_WIDTH, paint.measureText("a"), 0.0f); + assertEquals(GLYPH_1EM_WIDTH, paint.measureText("b"), 0.0f); + assertEquals(GLYPH_1EM_WIDTH, paint.measureText("c"), 0.0f); + + testTypeface = fontMap.get("google-sans"); + assertNotNull(testTypeface); + paint.setTypeface(testTypeface); + assertEquals(GLYPH_1EM_WIDTH, paint.measureText("a"), 0.0f); + assertEquals(GLYPH_3EM_WIDTH, paint.measureText("b"), 0.0f); + assertEquals(GLYPH_1EM_WIDTH, paint.measureText("c"), 0.0f); + } + + @Test + public void testBuildSystemFallback__Customization_new_named_family_override() { + final String xml = "<?xml version='1.0' encoding='UTF-8'?>" + + "<familyset>" + + " <family name='sans-serif'>" + + " <font weight='400' style='normal'>a3em.ttf</font>" + + " </family>" + + "</familyset>"; + final String oemXml = "<?xml version='1.0' encoding='UTF-8'?>" + + "<fonts-modification version='1'>" + + " <family customizationType='new-named-family' name='sans-serif'>" + + " <font weight='400' style='normal'>b3em.ttf</font>" + + " </family>" + + "</fonts-modification>"; + final ArrayMap<String, Typeface> fontMap = new ArrayMap<>(); + final ArrayMap<String, FontFamily[]> fallbackMap = new ArrayMap<>(); + final FontCustomizationParser.Result oemCustomization = + readFontCustomization(oemXml); + + buildSystemFallback(xml, oemCustomization, fontMap, fallbackMap); + + final Paint paint = new Paint(); + + Typeface testTypeface = fontMap.get("sans-serif"); + assertNotNull(testTypeface); + paint.setTypeface(testTypeface); + assertEquals(GLYPH_1EM_WIDTH, paint.measureText("a"), 0.0f); + assertEquals(GLYPH_3EM_WIDTH, paint.measureText("b"), 0.0f); + assertEquals(GLYPH_1EM_WIDTH, paint.measureText("c"), 0.0f); + } + + @Test + public void testBuildSystemFallback__Customization_additional_alias() { + final String xml = "<?xml version='1.0' encoding='UTF-8'?>" + + "<familyset>" + + " <family name='sans-serif'>" + + " <font weight='400' style='normal'>a3em.ttf</font>" + + " </family>" + + "</familyset>"; + final String oemXml = "<?xml version='1.0' encoding='UTF-8'?>" + + "<fonts-modification version='1'>" + + " <family customizationType='new-named-family' name='google-sans'>" + + " <font weight='400' style='normal'>b3em.ttf</font>" + + " <font weight='700' style='normal'>c3em.ttf</font>" + + " </family>" + + " <alias name='another-google-sans' to='google-sans' />" + + " <alias name='google-sans-bold' to='google-sans' weight='700' />" + + "</fonts-modification>"; + final ArrayMap<String, Typeface> fontMap = new ArrayMap<>(); + final ArrayMap<String, FontFamily[]> fallbackMap = new ArrayMap<>(); + final FontCustomizationParser.Result oemCustomization = + readFontCustomization(oemXml); + + buildSystemFallback(xml, oemCustomization, fontMap, fallbackMap); + + final Paint paint = new Paint(); + + Typeface testTypeface = fontMap.get("sans-serif"); + assertNotNull(testTypeface); + paint.setTypeface(testTypeface); + assertEquals(GLYPH_3EM_WIDTH, paint.measureText("a"), 0.0f); + assertEquals(GLYPH_1EM_WIDTH, paint.measureText("b"), 0.0f); + assertEquals(GLYPH_1EM_WIDTH, paint.measureText("c"), 0.0f); + + testTypeface = fontMap.get("google-sans"); + assertNotNull(testTypeface); + paint.setTypeface(testTypeface); + assertEquals(GLYPH_1EM_WIDTH, paint.measureText("a"), 0.0f); + assertEquals(GLYPH_3EM_WIDTH, paint.measureText("b"), 0.0f); + assertEquals(GLYPH_1EM_WIDTH, paint.measureText("c"), 0.0f); + + testTypeface = fontMap.get("another-google-sans"); + assertNotNull(testTypeface); + paint.setTypeface(testTypeface); + assertEquals(GLYPH_1EM_WIDTH, paint.measureText("a"), 0.0f); + assertEquals(GLYPH_3EM_WIDTH, paint.measureText("b"), 0.0f); + assertEquals(GLYPH_1EM_WIDTH, paint.measureText("c"), 0.0f); + + testTypeface = fontMap.get("google-sans-bold"); + assertNotNull(testTypeface); + paint.setTypeface(testTypeface); + assertEquals(GLYPH_1EM_WIDTH, paint.measureText("a"), 0.0f); + assertEquals(GLYPH_1EM_WIDTH, paint.measureText("b"), 0.0f); + assertEquals(GLYPH_3EM_WIDTH, paint.measureText("c"), 0.0f); + } + + @Test + public void testBuildSystemFallback__Customization_additional_alias_conflict_with_new_name() { + final String xml = "<?xml version='1.0' encoding='UTF-8'?>" + + "<familyset>" + + " <family name='named-family'>" + + " <font weight='400' style='normal'>a3em.ttf</font>" + + " </family>" + + " <alias name='named-alias' to='named-family' />" + + "</familyset>"; + final String oemXml = "<?xml version='1.0' encoding='UTF-8'?>" + + "<fonts-modification version='1'>" + + " <family customizationType='new-named-family' name='named-alias'>" + + " <font weight='400' style='normal'>b3em.ttf</font>" + + " </family>" + + "</fonts-modification>"; + final ArrayMap<String, Typeface> fontMap = new ArrayMap<>(); + final ArrayMap<String, FontFamily[]> fallbackMap = new ArrayMap<>(); + final FontCustomizationParser.Result oemCustomization = + readFontCustomization(oemXml); + + buildSystemFallback(xml, oemCustomization, fontMap, fallbackMap); + + final Paint paint = new Paint(); + + Typeface testTypeface = fontMap.get("named-family"); + assertNotNull(testTypeface); + paint.setTypeface(testTypeface); + assertEquals(GLYPH_3EM_WIDTH, paint.measureText("a"), 0.0f); + assertEquals(GLYPH_1EM_WIDTH, paint.measureText("b"), 0.0f); + assertEquals(GLYPH_1EM_WIDTH, paint.measureText("c"), 0.0f); + + testTypeface = fontMap.get("named-alias"); + assertNotNull(testTypeface); + paint.setTypeface(testTypeface); + assertEquals(GLYPH_1EM_WIDTH, paint.measureText("a"), 0.0f); + assertEquals(GLYPH_3EM_WIDTH, paint.measureText("b"), 0.0f); + assertEquals(GLYPH_1EM_WIDTH, paint.measureText("c"), 0.0f); + } + + @Test(expected = IllegalArgumentException.class) + public void testBuildSystemFallback__Customization_new_named_family_no_name_exception() { + final String oemXml = "<?xml version='1.0' encoding='UTF-8'?>" + + "<fonts-modification version='1'>" + + " <family customizationType='new-named-family'>" + + " <font weight='400' style='normal'>b3em.ttf</font>" + + " </family>" + + "</fonts-modification>"; + readFontCustomization(oemXml); + } + + @Test(expected = IllegalArgumentException.class) + public void testBuildSystemFallback__Customization_new_named_family_dup_name_exception() { + final String oemXml = "<?xml version='1.0' encoding='UTF-8'?>" + + "<fonts-modification version='1'>" + + " <family customizationType='new-named-family' name='google-sans'>" + + " <font weight='400' style='normal'>b3em.ttf</font>" + + " </family>" + + " <family customizationType='new-named-family' name='google-sans'>" + + " <font weight='400' style='normal'>b3em.ttf</font>" + + " </family>" + + "</fonts-modification>"; + readFontCustomization(oemXml); + } } diff --git a/core/tests/coretests/src/android/net/LocalSocketTest.java b/core/tests/coretests/src/android/net/LocalSocketTest.java index 1349844c80cf..1286b137cc1f 100644 --- a/core/tests/coretests/src/android/net/LocalSocketTest.java +++ b/core/tests/coretests/src/android/net/LocalSocketTest.java @@ -22,6 +22,7 @@ import android.net.LocalSocket; import android.net.LocalSocketAddress; import android.test.MoreAsserts; import android.test.suitebuilder.annotation.SmallTest; + import junit.framework.TestCase; import java.io.FileDescriptor; @@ -39,6 +40,20 @@ public class LocalSocketTest extends TestCase { ls = new LocalSocket(); + try { + ls.connect(new LocalSocketAddress(null)); + fail("Expected NullPointerException"); + } catch (NullPointerException e) { + // pass + } + + try { + ls.bind(new LocalSocketAddress(null)); + fail("Expected NullPointerException"); + } catch (NullPointerException e) { + // pass + } + ls.connect(new LocalSocketAddress("android.net.LocalSocketTest")); ls1 = ss.accept(); diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java index 8f0e76bdc0c9..4ecdc42e3372 100644 --- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java +++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java @@ -114,6 +114,7 @@ public class SettingsBackupTest { Settings.Global.ANOMALY_CONFIG_VERSION, Settings.Global.APN_DB_UPDATE_CONTENT_URL, Settings.Global.APN_DB_UPDATE_METADATA_URL, + Settings.Global.APP_BINDING_CONSTANTS, Settings.Global.APP_IDLE_CONSTANTS, Settings.Global.APP_OPS_CONSTANTS, Settings.Global.APP_STANDBY_ENABLED, @@ -462,6 +463,7 @@ public class SettingsBackupTest { Settings.Global.WFC_IMS_MODE, Settings.Global.WFC_IMS_ROAMING_ENABLED, Settings.Global.WFC_IMS_ROAMING_MODE, + Settings.Global.WIFI_ALWAYS_REQUESTED, Settings.Global.WIFI_BADGING_THRESHOLDS, Settings.Global.WIFI_BOUNCE_DELAY_OVERRIDE_MS, Settings.Global.WIFI_CONNECTED_MAC_RANDOMIZATION_ENABLED, diff --git a/core/tests/coretests/src/android/text/FontFallbackSetup.java b/core/tests/coretests/src/android/text/FontFallbackSetup.java index 898e78c651e6..5592aac5a7c0 100644 --- a/core/tests/coretests/src/android/text/FontFallbackSetup.java +++ b/core/tests/coretests/src/android/text/FontFallbackSetup.java @@ -21,6 +21,7 @@ import android.content.Context; import android.content.res.AssetManager; import android.graphics.Typeface; import android.graphics.fonts.Font; +import android.graphics.fonts.FontCustomizationParser; import android.graphics.fonts.FontFamily; import android.graphics.fonts.SystemFonts; import android.support.test.InstrumentationRegistry; @@ -77,8 +78,10 @@ public class FontFallbackSetup implements AutoCloseable { final ArrayMap<String, FontFamily[]> fallbackMap = new ArrayMap<>(); final ArrayList<Font> availableFonts = new ArrayList<>(); + final FontCustomizationParser.Result oemCustomization = + new FontCustomizationParser.Result(); final FontConfig.Alias[] aliases = SystemFonts.buildSystemFallback(testFontsXml, - mTestFontsDir, fallbackMap, availableFonts); + mTestFontsDir, oemCustomization, fallbackMap, availableFonts); Typeface.initSystemDefaultTypefaces(mFontMap, fallbackMap, aliases); } diff --git a/core/tests/coretests/src/com/android/internal/os/KernelCpuProcReaderTest.java b/core/tests/coretests/src/com/android/internal/os/KernelCpuProcReaderTest.java index efdd7e978853..8360126f3751 100644 --- a/core/tests/coretests/src/com/android/internal/os/KernelCpuProcReaderTest.java +++ b/core/tests/coretests/src/com/android/internal/os/KernelCpuProcReaderTest.java @@ -43,7 +43,7 @@ import java.util.Random; /** * Test class for {@link KernelCpuProcReader}. * - * $ atest FrameworksCoreTests:com.android.internal.os.KernelCpuProcReader + * $ atest FrameworksCoreTests:com.android.internal.os.KernelCpuProcReaderTest */ @SmallTest @RunWith(AndroidJUnit4.class) diff --git a/core/tests/coretests/src/com/android/internal/os/StoragedUidIoStatsReaderTest.java b/core/tests/coretests/src/com/android/internal/os/StoragedUidIoStatsReaderTest.java new file mode 100644 index 000000000000..c051a1cdf052 --- /dev/null +++ b/core/tests/coretests/src/com/android/internal/os/StoragedUidIoStatsReaderTest.java @@ -0,0 +1,171 @@ +/* + * 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.internal.os; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.mockito.Mockito.verifyZeroInteractions; + +import android.content.Context; +import android.os.FileUtils; +import android.support.test.InstrumentationRegistry; +import android.support.test.filters.SmallTest; +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.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import java.io.BufferedWriter; +import java.io.File; +import java.nio.file.Files; + + +/** + * Test class for {@link StoragedUidIoStatsReader}. + * + * To run it: + * atest FrameworksCoreTests:com.android.internal.os.StoragedUidIoStatsReaderTest + */ +@SmallTest +@RunWith(AndroidJUnit4.class) +public class StoragedUidIoStatsReaderTest { + + private File mRoot; + private File mTestDir; + private File mTestFile; + // private Random mRand = new Random(); + + private StoragedUidIoStatsReader mStoragedUidIoStatsReader; + @Mock + private StoragedUidIoStatsReader.Callback mCallback; + + private Context getContext() { + return InstrumentationRegistry.getContext(); + } + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + mTestDir = getContext().getDir("test", Context.MODE_PRIVATE); + mRoot = getContext().getFilesDir(); + mTestFile = new File(mTestDir, "test.file"); + mStoragedUidIoStatsReader = new StoragedUidIoStatsReader(mTestFile.getAbsolutePath()); + } + + @After + public void tearDown() throws Exception { + FileUtils.deleteContents(mTestDir); + FileUtils.deleteContents(mRoot); + } + + + /** + * Tests that reading will never call the callback. + */ + @Test + public void testReadNonexistentFile() throws Exception { + mStoragedUidIoStatsReader.readAbsolute(mCallback); + verifyZeroInteractions(mCallback); + + } + + /** + * Tests that reading a file with 3 uids works as expected. + */ + @Test + public void testReadExpected() throws Exception { + BufferedWriter bufferedWriter = Files.newBufferedWriter(mTestFile.toPath()); + int[] uids = {0, 100, 200}; + long[] fg_chars_read = {1L, 101L, 201L}; + long[] fg_chars_write = {2L, 102L, 202L}; + long[] fg_bytes_read = {3L, 103L, 203L}; + long[] fg_bytes_write = {4L, 104L, 204L}; + long[] bg_chars_read = {5L, 105L, 205L}; + long[] bg_chars_write = {6L, 106L, 206L}; + long[] bg_bytes_read = {7L, 107L, 207L}; + long[] bg_bytes_write = {8L, 108L, 208L}; + long[] fg_fsync = {9L, 109L, 209L}; + long[] bg_fsync = {10L, 110L, 210L}; + + for (int i = 0; i < uids.length; i++) { + bufferedWriter.write(String + .format("%d %d %d %d %d %d %d %d %d %d %d\n", uids[i], fg_chars_read[i], + fg_chars_write[i], fg_bytes_read[i], fg_bytes_write[i], + bg_chars_read[i], bg_chars_write[i], bg_bytes_read[i], + bg_bytes_write[i], fg_fsync[i], bg_fsync[i])); + } + bufferedWriter.close(); + + mStoragedUidIoStatsReader.readAbsolute(mCallback); + for (int i = 0; i < uids.length; i++) { + verify(mCallback).onUidStorageStats(uids[i], fg_chars_read[i], fg_chars_write[i], + fg_bytes_read[i], fg_bytes_write[i], bg_chars_read[i], bg_chars_write[i], + bg_bytes_read[i], bg_bytes_write[i], fg_fsync[i], bg_fsync[i]); + } + verifyNoMoreInteractions(mCallback); + + } + + /** + * Tests that a line with less than 11 items is passed over. + */ + @Test + public void testLineDoesNotElevenEntries() throws Exception { + BufferedWriter bufferedWriter = Files.newBufferedWriter(mTestFile.toPath()); + + // Only has 10 numbers. + bufferedWriter.write(String + .format("%d %d %d %d %d %d %d %d %d %d\n", 0, 1, 2, 3, 4, 5, 6, 7, 8, 9)); + + bufferedWriter.write(String + .format("%d %d %d %d %d %d %d %d %d %d %d\n", 10, 11, 12, 13, 14, 15, 16, 17, 18, + 19, 20)); + bufferedWriter.close(); + + // Make sure we get the second line, but the first is skipped. + mStoragedUidIoStatsReader.readAbsolute(mCallback); + verify(mCallback).onUidStorageStats(10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20); + verifyNoMoreInteractions(mCallback); + } + + + /** + * Tests that a line that is malformed is passed over. + */ + @Test + public void testLineIsMalformed() throws Exception { + BufferedWriter bufferedWriter = Files.newBufferedWriter(mTestFile.toPath()); + + // Line is not formatted properly. It has a string. + bufferedWriter.write(String + .format("%d %d %d %d %d %s %d %d %d %d %d\n", 0, 1, 2, 3, 4, "NotANumber", 5, 6, 7, + 8, 9)); + + bufferedWriter.write(String + .format("%d %d %d %d %d %d %d %d %d %d %d\n", 10, 11, 12, 13, 14, 15, 16, 17, 18, + 19, 20)); + bufferedWriter.close(); + + // Make sure we get the second line, but the first is skipped. + mStoragedUidIoStatsReader.readAbsolute(mCallback); + verify(mCallback).onUidStorageStats(10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20); + verifyNoMoreInteractions(mCallback); + } +} diff --git a/data/fonts/Android.mk b/data/fonts/Android.mk index 76eb4e676923..454dceb9c82c 100644 --- a/data/fonts/Android.mk +++ b/data/fonts/Android.mk @@ -89,23 +89,7 @@ include $(CLEAR_VARS) LOCAL_MODULE := fonts.xml LOCAL_MODULE_CLASS := ETC - -AOSP_FONTS_FILE := frameworks/base/data/fonts/fonts.xml - -ifdef ADDITIONAL_FONTS_FILE -ADDITIONAL_FONTS_SCRIPT := frameworks/base/tools/fonts/add_additional_fonts.py -ADD_ADDITIONAL_FONTS := $(local-generated-sources-dir)/fonts.xml - -$(ADD_ADDITIONAL_FONTS): PRIVATE_SCRIPT := $(ADDITIONAL_FONTS_SCRIPT) -$(ADD_ADDITIONAL_FONTS): PRIVATE_ADDITIONAL_FONTS_FILE := $(ADDITIONAL_FONTS_FILE) -$(ADD_ADDITIONAL_FONTS): $(ADDITIONAL_FONTS_SCRIPT) $(AOSP_FONTS_FILE) $(ADDITIONAL_FONTS_FILE) - rm -f $@ - python $(PRIVATE_SCRIPT) $@ $(PRIVATE_ADDITIONAL_FONTS_FILE) -else -ADD_ADDITIONAL_FONTS := $(AOSP_FONTS_FILE) -endif - -LOCAL_PREBUILT_MODULE_FILE := $(ADD_ADDITIONAL_FONTS) +LOCAL_PREBUILT_MODULE_FILE := frameworks/base/data/fonts/fonts.xml include $(BUILD_PREBUILT) diff --git a/graphics/java/android/graphics/FontListParser.java b/graphics/java/android/graphics/FontListParser.java index 82435d5511f5..21cc3757a40e 100644 --- a/graphics/java/android/graphics/FontListParser.java +++ b/graphics/java/android/graphics/FontListParser.java @@ -40,17 +40,25 @@ public class FontListParser { /* Parse fallback list (no names) */ @UnsupportedAppUsage public static FontConfig parse(InputStream in) throws XmlPullParserException, IOException { + return parse(in, "/system/fonts"); + } + + /** + * Parse the fonts.xml + */ + public static FontConfig parse(InputStream in, String fontDir) + throws XmlPullParserException, IOException { try { XmlPullParser parser = Xml.newPullParser(); parser.setInput(in, null); parser.nextTag(); - return readFamilies(parser); + return readFamilies(parser, fontDir); } finally { in.close(); } } - private static FontConfig readFamilies(XmlPullParser parser) + private static FontConfig readFamilies(XmlPullParser parser, String fontDir) throws XmlPullParserException, IOException { List<FontConfig.Family> families = new ArrayList<>(); List<FontConfig.Alias> aliases = new ArrayList<>(); @@ -60,7 +68,7 @@ public class FontListParser { if (parser.getEventType() != XmlPullParser.START_TAG) continue; String tag = parser.getName(); if (tag.equals("family")) { - families.add(readFamily(parser)); + families.add(readFamily(parser, fontDir)); } else if (tag.equals("alias")) { aliases.add(readAlias(parser)); } else { @@ -71,7 +79,10 @@ public class FontListParser { aliases.toArray(new FontConfig.Alias[aliases.size()])); } - private static FontConfig.Family readFamily(XmlPullParser parser) + /** + * Reads a family element + */ + public static FontConfig.Family readFamily(XmlPullParser parser, String fontDir) throws XmlPullParserException, IOException { final String name = parser.getAttributeValue(null, "name"); final String lang = parser.getAttributeValue("", "lang"); @@ -81,7 +92,7 @@ public class FontListParser { if (parser.getEventType() != XmlPullParser.START_TAG) continue; final String tag = parser.getName(); if (tag.equals("font")) { - fonts.add(readFont(parser)); + fonts.add(readFont(parser, fontDir)); } else { skip(parser); } @@ -102,7 +113,7 @@ public class FontListParser { private static final Pattern FILENAME_WHITESPACE_PATTERN = Pattern.compile("^[ \\n\\r\\t]+|[ \\n\\r\\t]+$"); - private static FontConfig.Font readFont(XmlPullParser parser) + private static FontConfig.Font readFont(XmlPullParser parser, String fontDir) throws XmlPullParserException, IOException { String indexStr = parser.getAttributeValue(null, "index"); int index = indexStr == null ? 0 : Integer.parseInt(indexStr); @@ -125,7 +136,7 @@ public class FontListParser { } } String sanitizedName = FILENAME_WHITESPACE_PATTERN.matcher(filename).replaceAll(""); - return new FontConfig.Font(sanitizedName, index, axes.toArray( + return new FontConfig.Font(fontDir + sanitizedName, index, axes.toArray( new FontVariationAxis[axes.size()]), weight, isItalic, fallbackFor); } @@ -137,7 +148,10 @@ public class FontListParser { return new FontVariationAxis(tagStr, Float.parseFloat(styleValueStr)); } - private static FontConfig.Alias readAlias(XmlPullParser parser) + /** + * Reads alias elements + */ + public static FontConfig.Alias readAlias(XmlPullParser parser) throws XmlPullParserException, IOException { String name = parser.getAttributeValue(null, "name"); String toName = parser.getAttributeValue(null, "to"); @@ -152,7 +166,10 @@ public class FontListParser { return new FontConfig.Alias(name, toName, weight); } - private static void skip(XmlPullParser parser) throws XmlPullParserException, IOException { + /** + * Skip until next element + */ + public static void skip(XmlPullParser parser) throws XmlPullParserException, IOException { int depth = 1; while (depth > 0) { switch (parser.next()) { diff --git a/graphics/java/android/graphics/Rect.java b/graphics/java/android/graphics/Rect.java index 4fec33f19d92..c4dc0adb3be0 100644 --- a/graphics/java/android/graphics/Rect.java +++ b/graphics/java/android/graphics/Rect.java @@ -23,8 +23,11 @@ import android.annotation.UnsupportedAppUsage; import android.os.Parcel; import android.os.Parcelable; import android.text.TextUtils; +import android.util.proto.ProtoInputStream; import android.util.proto.ProtoOutputStream; +import android.util.proto.WireTypeMismatchException; +import java.io.IOException; import java.io.PrintWriter; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -232,6 +235,40 @@ public final class Rect implements Parcelable { } /** + * Read from a protocol buffer input stream. + * Protocol buffer message definition at {@link android.graphics.RectProto} + * + * @param proto Stream to read the Rect object from. + * @param fieldId Field Id of the Rect as defined in the parent message + * @hide + */ + public void readFromProto(@NonNull ProtoInputStream proto, long fieldId) throws IOException, + WireTypeMismatchException { + final long token = proto.start(fieldId); + try { + while (proto.nextField() != ProtoInputStream.NO_MORE_FIELDS) { + switch (proto.getFieldNumber()) { + case (int) RectProto.LEFT: + left = proto.readInt(RectProto.LEFT); + break; + case (int) RectProto.TOP: + top = proto.readInt(RectProto.TOP); + break; + case (int) RectProto.RIGHT: + right = proto.readInt(RectProto.RIGHT); + break; + case (int) RectProto.BOTTOM: + bottom = proto.readInt(RectProto.BOTTOM); + break; + } + } + } finally { + // Let caller handle any exceptions + proto.end(token); + } + } + + /** * Returns true if the rectangle is empty (left >= right or top >= bottom) */ public final boolean isEmpty() { diff --git a/graphics/java/android/graphics/Typeface.java b/graphics/java/android/graphics/Typeface.java index e6ac06011e23..7ad207f339d1 100644 --- a/graphics/java/android/graphics/Typeface.java +++ b/graphics/java/android/graphics/Typeface.java @@ -1103,6 +1103,9 @@ public class Typeface { } for (FontConfig.Alias alias : aliases) { + if (systemFontMap.containsKey(alias.getName())) { + continue; // If alias and named family are conflict, use named family. + } final Typeface base = systemFontMap.get(alias.getToName()); final int weight = alias.getWeight(); final Typeface newFace = weight == 400 ? base : diff --git a/graphics/java/android/graphics/fonts/FontCustomizationParser.java b/graphics/java/android/graphics/fonts/FontCustomizationParser.java new file mode 100644 index 000000000000..0291d7484dc5 --- /dev/null +++ b/graphics/java/android/graphics/fonts/FontCustomizationParser.java @@ -0,0 +1,105 @@ +/* + * 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.graphics.fonts; + +import android.annotation.NonNull; +import android.graphics.FontListParser; +import android.text.FontConfig; +import android.util.Xml; + +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; + +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.HashSet; + +/** + * Parser for font customization + * + * @hide + */ +public class FontCustomizationParser { + /** + * Represents a customization XML + */ + public static class Result { + ArrayList<FontConfig.Family> mAdditionalNamedFamilies = new ArrayList<>(); + ArrayList<FontConfig.Alias> mAdditionalAliases = new ArrayList<>(); + } + + /** + * Parses the customization XML + * + * Caller must close the input stream + */ + public static Result parse(@NonNull InputStream in, @NonNull String fontDir) + throws XmlPullParserException, IOException { + XmlPullParser parser = Xml.newPullParser(); + parser.setInput(in, null); + parser.nextTag(); + return readFamilies(parser, fontDir); + } + + private static void validate(Result result) { + HashSet<String> familyNames = new HashSet<>(); + for (int i = 0; i < result.mAdditionalNamedFamilies.size(); ++i) { + final FontConfig.Family family = result.mAdditionalNamedFamilies.get(i); + final String name = family.getName(); + if (name == null) { + throw new IllegalArgumentException("new-named-family requires name attribute"); + } + if (!familyNames.add(name)) { + throw new IllegalArgumentException( + "new-named-family requires unique name attribute"); + } + } + } + + private static Result readFamilies(XmlPullParser parser, String fontDir) + throws XmlPullParserException, IOException { + Result out = new Result(); + parser.require(XmlPullParser.START_TAG, null, "fonts-modification"); + while (parser.next() != XmlPullParser.END_TAG) { + if (parser.getEventType() != XmlPullParser.START_TAG) continue; + String tag = parser.getName(); + if (tag.equals("family")) { + readFamily(parser, fontDir, out); + } else if (tag.equals("alias")) { + out.mAdditionalAliases.add(FontListParser.readAlias(parser)); + } else { + FontListParser.skip(parser); + } + } + validate(out); + return out; + } + + private static void readFamily(XmlPullParser parser, String fontDir, Result out) + throws XmlPullParserException, IOException { + final String customizationType = parser.getAttributeValue(null, "customizationType"); + if (customizationType == null) { + throw new IllegalArgumentException("customizationType must be specified"); + } + if (customizationType.equals("new-named-family")) { + out.mAdditionalNamedFamilies.add(FontListParser.readFamily(parser, fontDir)); + } else { + throw new IllegalArgumentException("Unknown customizationType=" + customizationType); + } + } +} diff --git a/graphics/java/android/graphics/fonts/SystemFonts.java b/graphics/java/android/graphics/fonts/SystemFonts.java index 5e80749e9233..2d21bbbd4e43 100644 --- a/graphics/java/android/graphics/fonts/SystemFonts.java +++ b/graphics/java/android/graphics/fonts/SystemFonts.java @@ -113,7 +113,6 @@ public final class SystemFonts { private static void pushFamilyToFallback(@NonNull FontConfig.Family xmlFamily, @NonNull ArrayMap<String, ArrayList<FontFamily>> fallbackMap, @NonNull Map<String, ByteBuffer> cache, - @NonNull String fontDir, @NonNull ArrayList<Font> availableFonts) { final String languageTags = xmlFamily.getLanguages(); @@ -138,8 +137,7 @@ public final class SystemFonts { } final FontFamily defaultFamily = defaultFonts.isEmpty() ? null : createFontFamily( - xmlFamily.getName(), defaultFonts, languageTags, variant, cache, fontDir, - availableFonts); + xmlFamily.getName(), defaultFonts, languageTags, variant, cache, availableFonts); // Insert family into fallback map. for (int i = 0; i < fallbackMap.size(); i++) { @@ -151,7 +149,7 @@ public final class SystemFonts { } } else { final FontFamily family = createFontFamily( - xmlFamily.getName(), fallback, languageTags, variant, cache, fontDir, + xmlFamily.getName(), fallback, languageTags, variant, cache, availableFonts); if (family != null) { fallbackMap.valueAt(i).add(family); @@ -169,7 +167,6 @@ public final class SystemFonts { @NonNull String languageTags, @FontConfig.Family.Variant int variant, @NonNull Map<String, ByteBuffer> cache, - @NonNull String fontDir, @NonNull ArrayList<Font> availableFonts) { if (fonts.size() == 0) { return null; @@ -178,7 +175,7 @@ public final class SystemFonts { FontFamily.Builder b = null; for (int i = 0; i < fonts.size(); i++) { final FontConfig.Font fontConfig = fonts.get(i); - final String fullPath = fontDir + fontConfig.getFontName(); + final String fullPath = fontConfig.getFontName(); ByteBuffer buffer = cache.get(fullPath); if (buffer == null) { if (cache.containsKey(fullPath)) { @@ -213,6 +210,22 @@ public final class SystemFonts { return b == null ? null : b.build(languageTags, variant); } + private static void appendNamedFamily(@NonNull FontConfig.Family xmlFamily, + @NonNull HashMap<String, ByteBuffer> bufferCache, + @NonNull ArrayMap<String, ArrayList<FontFamily>> fallbackListMap, + @NonNull ArrayList<Font> availableFonts) { + final String familyName = xmlFamily.getName(); + final FontFamily family = createFontFamily( + familyName, Arrays.asList(xmlFamily.getFonts()), + xmlFamily.getLanguages(), xmlFamily.getVariant(), bufferCache, availableFonts); + if (family == null) { + return; + } + final ArrayList<FontFamily> fallback = new ArrayList<>(); + fallback.add(family); + fallbackListMap.put(familyName, fallback); + } + /** * Build the system fallback from xml file. * @@ -226,11 +239,12 @@ public final class SystemFonts { @VisibleForTesting public static FontConfig.Alias[] buildSystemFallback(@NonNull String xmlPath, @NonNull String fontDir, + @NonNull FontCustomizationParser.Result oemCustomization, @NonNull ArrayMap<String, FontFamily[]> fallbackMap, @NonNull ArrayList<Font> availableFonts) { try { final FileInputStream fontsIn = new FileInputStream(xmlPath); - final FontConfig fontConfig = FontListParser.parse(fontsIn); + final FontConfig fontConfig = FontListParser.parse(fontsIn, fontDir); final HashMap<String, ByteBuffer> bufferCache = new HashMap<String, ByteBuffer>(); final FontConfig.Family[] xmlFamilies = fontConfig.getFamilies(); @@ -242,16 +256,12 @@ public final class SystemFonts { if (familyName == null) { continue; } - final FontFamily family = createFontFamily( - xmlFamily.getName(), Arrays.asList(xmlFamily.getFonts()), - xmlFamily.getLanguages(), xmlFamily.getVariant(), bufferCache, fontDir, - availableFonts); - if (family == null) { - continue; - } - final ArrayList<FontFamily> fallback = new ArrayList<>(); - fallback.add(family); - fallbackListMap.put(familyName, fallback); + appendNamedFamily(xmlFamily, bufferCache, fallbackListMap, availableFonts); + } + + for (int i = 0; i < oemCustomization.mAdditionalNamedFamilies.size(); ++i) { + appendNamedFamily(oemCustomization.mAdditionalNamedFamilies.get(i), + bufferCache, fallbackListMap, availableFonts); } // Then, add fallback fonts to the each fallback map. @@ -260,8 +270,7 @@ public final class SystemFonts { // The first family (usually the sans-serif family) is always placed immediately // after the primary family in the fallback. if (i == 0 || xmlFamily.getName() == null) { - pushFamilyToFallback(xmlFamily, fallbackListMap, bufferCache, fontDir, - availableFonts); + pushFamilyToFallback(xmlFamily, fallbackListMap, bufferCache, availableFonts); } } @@ -274,20 +283,36 @@ public final class SystemFonts { fallbackMap.put(fallbackName, families); } - return fontConfig.getAliases(); + final ArrayList<FontConfig.Alias> list = new ArrayList<>(); + list.addAll(Arrays.asList(fontConfig.getAliases())); + list.addAll(oemCustomization.mAdditionalAliases); + return list.toArray(new FontConfig.Alias[list.size()]); } catch (IOException | XmlPullParserException e) { Log.e(TAG, "Failed initialize system fallbacks.", e); return ArrayUtils.emptyArray(FontConfig.Alias.class); } } + private static FontCustomizationParser.Result readFontCustomization( + @NonNull String customizeXml, @NonNull String customFontsDir) { + try (FileInputStream f = new FileInputStream(customizeXml)) { + return FontCustomizationParser.parse(f, customFontsDir); + } catch (IOException e) { + return new FontCustomizationParser.Result(); + } catch (XmlPullParserException e) { + Log.e(TAG, "Failed to parse font customization XML", e); + return new FontCustomizationParser.Result(); + } + } + static { final ArrayMap<String, FontFamily[]> systemFallbackMap = new ArrayMap<>(); final ArrayList<Font> availableFonts = new ArrayList<>(); + final FontCustomizationParser.Result oemCustomization = + readFontCustomization("/product/etc/fonts_customization.xml", "/product/fonts/"); sAliases = buildSystemFallback("/system/etc/fonts.xml", "/system/fonts/", - systemFallbackMap, availableFonts); + oemCustomization, systemFallbackMap, availableFonts); sSystemFallbackMap = Collections.unmodifiableMap(systemFallbackMap); sAvailableFonts = Collections.unmodifiableList(availableFonts); } - } diff --git a/libs/androidfw/include/androidfw/Util.h b/libs/androidfw/include/androidfw/Util.h index e4cd6a8ab6b8..6c9eee0b8835 100644 --- a/libs/androidfw/include/androidfw/Util.h +++ b/libs/androidfw/include/androidfw/Util.h @@ -47,11 +47,11 @@ class unique_cptr { constexpr unique_cptr() : ptr_(nullptr) {} constexpr unique_cptr(std::nullptr_t) : ptr_(nullptr) {} explicit unique_cptr(pointer ptr) : ptr_(ptr) {} - unique_cptr(unique_cptr&& o) : ptr_(o.ptr_) { o.ptr_ = nullptr; } + unique_cptr(unique_cptr&& o) noexcept : ptr_(o.ptr_) { o.ptr_ = nullptr; } ~unique_cptr() { std::free(reinterpret_cast<void*>(ptr_)); } - inline unique_cptr& operator=(unique_cptr&& o) { + inline unique_cptr& operator=(unique_cptr&& o) noexcept { if (&o == this) { return *this; } diff --git a/libs/hwui/Layer.cpp b/libs/hwui/Layer.cpp index cc95051fc952..32aaa54e696c 100644 --- a/libs/hwui/Layer.cpp +++ b/libs/hwui/Layer.cpp @@ -77,5 +77,13 @@ void Layer::postDecStrong() { mRenderState.postDecStrong(this); } +SkBlendMode Layer::getMode() const { + if (mBlend || mode != SkBlendMode::kSrcOver) { + return mode; + } else { + return SkBlendMode::kSrc; + } +} + }; // namespace uirenderer }; // namespace android diff --git a/libs/hwui/Layer.h b/libs/hwui/Layer.h index 6f07a43ceb58..e4f96e914c36 100644 --- a/libs/hwui/Layer.h +++ b/libs/hwui/Layer.h @@ -67,7 +67,7 @@ public: inline int getAlpha() const { return alpha; } - inline SkBlendMode getMode() const { return mode; } + SkBlendMode getMode() const; inline SkColorFilter* getColorFilter() const { return mColorFilter.get(); } diff --git a/libs/hwui/RecordingCanvas.cpp b/libs/hwui/RecordingCanvas.cpp index c30af842ebbb..f928de9b92a6 100644 --- a/libs/hwui/RecordingCanvas.cpp +++ b/libs/hwui/RecordingCanvas.cpp @@ -23,6 +23,7 @@ #include "SkDrawShadowInfo.h" #include "SkImage.h" #include "SkImageFilter.h" +#include "SkLatticeIter.h" #include "SkMath.h" #include "SkPicture.h" #include "SkRSXform.h" @@ -280,7 +281,8 @@ struct DrawPicture final : Op { struct DrawImage final : Op { static const auto kType = Type::DrawImage; - DrawImage(sk_sp<const SkImage>&& image, SkScalar x, SkScalar y, const SkPaint* paint, BitmapPalette palette) + DrawImage(sk_sp<const SkImage>&& image, SkScalar x, SkScalar y, const SkPaint* paint, + BitmapPalette palette) : image(std::move(image)), x(x), y(y), palette(palette) { if (paint) { this->paint = *paint; @@ -312,7 +314,8 @@ struct DrawImageNine final : Op { struct DrawImageRect final : Op { static const auto kType = Type::DrawImageRect; DrawImageRect(sk_sp<const SkImage>&& image, const SkRect* src, const SkRect& dst, - const SkPaint* paint, SkCanvas::SrcRectConstraint constraint, BitmapPalette palette) + const SkPaint* paint, SkCanvas::SrcRectConstraint constraint, + BitmapPalette palette) : image(std::move(image)), dst(dst), constraint(constraint), palette(palette) { this->src = src ? *src : SkRect::MakeIWH(this->image->width(), this->image->height()); if (paint) { @@ -331,8 +334,14 @@ struct DrawImageRect final : Op { struct DrawImageLattice final : Op { static const auto kType = Type::DrawImageLattice; DrawImageLattice(sk_sp<const SkImage>&& image, int xs, int ys, int fs, const SkIRect& src, - const SkRect& dst, const SkPaint* paint) - : image(std::move(image)), xs(xs), ys(ys), fs(fs), src(src), dst(dst) { + const SkRect& dst, const SkPaint* paint, BitmapPalette palette) + : image(std::move(image)) + , xs(xs) + , ys(ys) + , fs(fs) + , src(src) + , dst(dst) + , palette(palette) { if (paint) { this->paint = *paint; } @@ -342,6 +351,7 @@ struct DrawImageLattice final : Op { SkIRect src; SkRect dst; SkPaint paint; + BitmapPalette palette; void draw(SkCanvas* c, const SkMatrix&) const { auto xdivs = pod<int>(this, 0), ydivs = pod<int>(this, xs * sizeof(int)); auto colors = (0 == fs) ? nullptr : pod<SkColor>(this, (xs + ys) * sizeof(int)); @@ -511,16 +521,13 @@ struct DrawVectorDrawable final : Op { tree->getPaintFor(&paint, tree->stagingProperties()); } - void draw(SkCanvas* canvas, const SkMatrix&) const { - mRoot->draw(canvas, mBounds, paint); - } + void draw(SkCanvas* canvas, const SkMatrix&) const { mRoot->draw(canvas, mBounds, paint); } sp<VectorDrawableRoot> mRoot; SkRect mBounds; SkPaint paint; BitmapPalette palette; }; - } template <typename T, typename... Args> @@ -647,14 +654,15 @@ void DisplayListData::drawImageRect(sk_sp<const SkImage> image, const SkRect* sr this->push<DrawImageRect>(0, std::move(image), src, dst, paint, constraint, palette); } void DisplayListData::drawImageLattice(sk_sp<const SkImage> image, const SkCanvas::Lattice& lattice, - const SkRect& dst, const SkPaint* paint) { + const SkRect& dst, const SkPaint* paint, + BitmapPalette palette) { int xs = lattice.fXCount, ys = lattice.fYCount; int fs = lattice.fRectTypes ? (xs + 1) * (ys + 1) : 0; size_t bytes = (xs + ys) * sizeof(int) + fs * sizeof(SkCanvas::Lattice::RectType) + fs * sizeof(SkColor); SkASSERT(lattice.fBounds); void* pod = this->push<DrawImageLattice>(bytes, std::move(image), xs, ys, fs, *lattice.fBounds, - dst, paint); + dst, paint, palette); copy_v(pod, lattice.fXDivs, xs, lattice.fYDivs, ys, lattice.fColors, fs, lattice.fRectTypes, fs); } @@ -779,22 +787,26 @@ constexpr bool has_palette = std::experimental::is_detected_v<has_palette_helper template <class T> constexpr color_transform_fn colorTransformForOp() { - if constexpr(has_paint<T> && has_palette<T>) { - // It's a bitmap - return [](const void* opRaw, ColorTransform transform) { - // TODO: We should be const. Or not. Or just use a different map - // Unclear, but this is the quick fix - const T* op = reinterpret_cast<const T*>(opRaw); - transformPaint(transform, const_cast<SkPaint*>(&(op->paint)), op->palette); - }; - } else if constexpr(has_paint<T>) { - return [](const void* opRaw, ColorTransform transform) { - // TODO: We should be const. Or not. Or just use a different map - // Unclear, but this is the quick fix - const T* op = reinterpret_cast<const T*>(opRaw); - transformPaint(transform, const_cast<SkPaint*>(&(op->paint))); - }; - } else { + if + constexpr(has_paint<T> && has_palette<T>) { + // It's a bitmap + return [](const void* opRaw, ColorTransform transform) { + // TODO: We should be const. Or not. Or just use a different map + // Unclear, but this is the quick fix + const T* op = reinterpret_cast<const T*>(opRaw); + transformPaint(transform, const_cast<SkPaint*>(&(op->paint)), op->palette); + }; + } + else if + constexpr(has_paint<T>) { + return [](const void* opRaw, ColorTransform transform) { + // TODO: We should be const. Or not. Or just use a different map + // Unclear, but this is the quick fix + const T* op = reinterpret_cast<const T*>(opRaw); + transformPaint(transform, const_cast<SkPaint*>(&(op->paint))); + }; + } + else { return nullptr; } } @@ -931,11 +943,12 @@ void RecordingCanvas::onDrawBitmapNine(const SkBitmap& bm, const SkIRect& center } void RecordingCanvas::onDrawBitmapRect(const SkBitmap& bm, const SkRect* src, const SkRect& dst, const SkPaint* paint, SrcRectConstraint constraint) { - fDL->drawImageRect(SkImage::MakeFromBitmap(bm), src, dst, paint, constraint, BitmapPalette::Unknown); + fDL->drawImageRect(SkImage::MakeFromBitmap(bm), src, dst, paint, constraint, + BitmapPalette::Unknown); } void RecordingCanvas::onDrawBitmapLattice(const SkBitmap& bm, const SkCanvas::Lattice& lattice, const SkRect& dst, const SkPaint* paint) { - fDL->drawImageLattice(SkImage::MakeFromBitmap(bm), lattice, dst, paint); + fDL->drawImageLattice(SkImage::MakeFromBitmap(bm), lattice, dst, paint, BitmapPalette::Unknown); } void RecordingCanvas::drawImage(const sk_sp<SkImage>& image, SkScalar x, SkScalar y, @@ -943,11 +956,34 @@ void RecordingCanvas::drawImage(const sk_sp<SkImage>& image, SkScalar x, SkScala fDL->drawImage(image, x, y, paint, palette); } -void RecordingCanvas::drawImageRect(const sk_sp<SkImage>& image, const SkRect& src, const SkRect& dst, - const SkPaint* paint, SrcRectConstraint constraint, BitmapPalette palette) { +void RecordingCanvas::drawImageRect(const sk_sp<SkImage>& image, const SkRect& src, + const SkRect& dst, const SkPaint* paint, + SrcRectConstraint constraint, BitmapPalette palette) { fDL->drawImageRect(image, &src, dst, paint, constraint, palette); } +void RecordingCanvas::drawImageLattice(const sk_sp<SkImage>& image, const Lattice& lattice, + const SkRect& dst, const SkPaint* paint, + BitmapPalette palette) { + if (!image || dst.isEmpty()) { + return; + } + + SkIRect bounds; + Lattice latticePlusBounds = lattice; + if (!latticePlusBounds.fBounds) { + bounds = SkIRect::MakeWH(image->width(), image->height()); + latticePlusBounds.fBounds = &bounds; + } + + if (SkLatticeIter::Valid(image->width(), image->height(), latticePlusBounds)) { + fDL->drawImageLattice(image, latticePlusBounds, dst, paint, palette); + } else { + fDL->drawImageRect(image, nullptr, dst, paint, SrcRectConstraint::kFast_SrcRectConstraint, + palette); + } +} + void RecordingCanvas::onDrawImage(const SkImage* img, SkScalar x, SkScalar y, const SkPaint* paint) { fDL->drawImage(sk_ref_sp(img), x, y, paint, BitmapPalette::Unknown); @@ -962,7 +998,7 @@ void RecordingCanvas::onDrawImageRect(const SkImage* img, const SkRect* src, con } void RecordingCanvas::onDrawImageLattice(const SkImage* img, const SkCanvas::Lattice& lattice, const SkRect& dst, const SkPaint* paint) { - fDL->drawImageLattice(sk_ref_sp(img), lattice, dst, paint); + fDL->drawImageLattice(sk_ref_sp(img), lattice, dst, paint, BitmapPalette::Unknown); } void RecordingCanvas::onDrawPatch(const SkPoint cubics[12], const SkColor colors[4], @@ -975,8 +1011,8 @@ void RecordingCanvas::onDrawPoints(SkCanvas::PointMode mode, size_t count, const fDL->drawPoints(mode, count, pts, paint); } void RecordingCanvas::onDrawVerticesObject(const SkVertices* vertices, - const SkVertices::Bone bones[], int boneCount, - SkBlendMode mode, const SkPaint& paint) { + const SkVertices::Bone bones[], int boneCount, + SkBlendMode mode, const SkPaint& paint) { fDL->drawVertices(vertices, bones, boneCount, mode, paint); } void RecordingCanvas::onDrawAtlas(const SkImage* atlas, const SkRSXform xforms[], diff --git a/libs/hwui/RecordingCanvas.h b/libs/hwui/RecordingCanvas.h index 80c80ca46515..099e0be433ea 100644 --- a/libs/hwui/RecordingCanvas.h +++ b/libs/hwui/RecordingCanvas.h @@ -110,7 +110,7 @@ private: void drawImageRect(sk_sp<const SkImage>, const SkRect*, const SkRect&, const SkPaint*, SkCanvas::SrcRectConstraint, BitmapPalette palette); void drawImageLattice(sk_sp<const SkImage>, const SkCanvas::Lattice&, const SkRect&, - const SkPaint*); + const SkPaint*, BitmapPalette); void drawPatch(const SkPoint[12], const SkColor[4], const SkPoint[4], SkBlendMode, const SkPaint&); @@ -185,11 +185,13 @@ public: void onDrawBitmapRect(const SkBitmap&, const SkRect*, const SkRect&, const SkPaint*, SrcRectConstraint) override; - void drawImage(const sk_sp<SkImage>& image, SkScalar left, SkScalar top, - const SkPaint* paint, BitmapPalette pallete); + void drawImage(const sk_sp<SkImage>& image, SkScalar left, SkScalar top, const SkPaint* paint, + BitmapPalette pallete); void drawImageRect(const sk_sp<SkImage>& image, const SkRect& src, const SkRect& dst, const SkPaint* paint, SrcRectConstraint constraint, BitmapPalette palette); + void drawImageLattice(const sk_sp<SkImage>& image, const Lattice& lattice, const SkRect& dst, + const SkPaint* paint, BitmapPalette palette); void onDrawImage(const SkImage*, SkScalar, SkScalar, const SkPaint*) override; void onDrawImageLattice(const SkImage*, const Lattice&, const SkRect&, const SkPaint*) override; diff --git a/libs/hwui/RenderNode.cpp b/libs/hwui/RenderNode.cpp index d9a7cc3a575f..d2a8f02cc6a7 100644 --- a/libs/hwui/RenderNode.cpp +++ b/libs/hwui/RenderNode.cpp @@ -282,25 +282,45 @@ void RenderNode::syncDisplayList(TreeObserver& observer, TreeInfo* info) { mStagingDisplayList = nullptr; if (mDisplayList) { mDisplayList->syncContents(); + handleForceDark(info); + } +} - if (CC_UNLIKELY(info && !info->disableForceDark)) { - auto usage = usageHint(); - if (mDisplayList->hasText()) { - usage = UsageHint::Foreground; - } - if (usage == UsageHint::Unknown) { - if (mDisplayList->mChildNodes.size() > 1) { - usage = UsageHint::Background; - } else if (mDisplayList->mChildNodes.size() == 1 && - mDisplayList->mChildNodes.front().getRenderNode()->usageHint() != - UsageHint::Background) { - usage = UsageHint::Background; - } +void RenderNode::handleForceDark(android::uirenderer::TreeInfo *info) { + if (CC_LIKELY(!info || info->disableForceDark)) { + return; + } + auto usage = usageHint(); + const auto& children = mDisplayList->mChildNodes; + if (mDisplayList->hasText()) { + usage = UsageHint::Foreground; + } + if (usage == UsageHint::Unknown) { + if (children.size() > 1) { + usage = UsageHint::Background; + } else if (children.size() == 1 && + children.front().getRenderNode()->usageHint() != + UsageHint::Background) { + usage = UsageHint::Background; + } + } + if (children.size() > 1) { + // Crude overlap check + SkRect drawn = SkRect::MakeEmpty(); + for (auto iter = children.rbegin(); iter != children.rend(); ++iter) { + const auto& child = iter->getRenderNode(); + // We use stagingProperties here because we haven't yet sync'd the children + SkRect bounds = SkRect::MakeXYWH(child->stagingProperties().getX(), child->stagingProperties().getY(), + child->stagingProperties().getWidth(), child->stagingProperties().getHeight()); + if (bounds.contains(drawn)) { + // This contains everything drawn after it, so make it a background + child->setUsageHint(UsageHint::Background); } - mDisplayList->mDisplayList.applyColorTransform( - usage == UsageHint::Background ? ColorTransform::Dark : ColorTransform::Light); + drawn.join(bounds); } } + mDisplayList->mDisplayList.applyColorTransform( + usage == UsageHint::Background ? ColorTransform::Dark : ColorTransform::Light); } void RenderNode::pushStagingDisplayListChanges(TreeObserver& observer, TreeInfo& info) { diff --git a/libs/hwui/RenderNode.h b/libs/hwui/RenderNode.h index 211dd2db5cf8..be0b46b1c45f 100644 --- a/libs/hwui/RenderNode.h +++ b/libs/hwui/RenderNode.h @@ -220,6 +220,7 @@ private: void syncProperties(); void syncDisplayList(TreeObserver& observer, TreeInfo* info); + void handleForceDark(TreeInfo* info); void prepareTreeImpl(TreeObserver& observer, TreeInfo& info, bool functorsNeedLayer); void pushStagingPropertiesChanges(TreeInfo& info); diff --git a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp index fac07d74dad4..3fa73a4dadda 100644 --- a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp +++ b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp @@ -245,8 +245,9 @@ void SkiaRecordingCanvas::drawNinePatch(Bitmap& bitmap, const Res_png_9patch& ch } sk_sp<SkColorFilter> colorFilter; sk_sp<SkImage> image = bitmap.makeImage(&colorFilter); - mRecorder.drawImageLattice(image.get(), lattice, dst, - filterBitmap(std::move(filteredPaint), std::move(colorFilter))); + mRecorder.drawImageLattice(image, lattice, dst, + filterBitmap(std::move(filteredPaint), std::move(colorFilter)), + bitmap.palette()); if (!bitmap.isImmutable() && image.get() && !image->unique() && !dst.isEmpty()) { mDisplayList->mMutableImages.push_back(image.get()); } diff --git a/location/java/android/location/Criteria.java b/location/java/android/location/Criteria.java index a6099be60875..74eb4450d42c 100644 --- a/location/java/android/location/Criteria.java +++ b/location/java/android/location/Criteria.java @@ -21,9 +21,9 @@ import android.os.Parcelable; /** * A class indicating the application criteria for selecting a - * location provider. Providers maybe ordered according to accuracy, - * power usage, ability to report altitude, speed, - * and bearing, and monetary cost. + * location provider. Providers may be ordered according to accuracy, + * power usage, ability to report altitude, speed, bearing, and monetary + * cost. */ public class Criteria implements Parcelable { /** diff --git a/location/lib/Android.bp b/location/lib/Android.bp index 447195d6d532..b09335c6707f 100644 --- a/location/lib/Android.bp +++ b/location/lib/Android.bp @@ -18,4 +18,5 @@ java_sdk_library { name: "com.android.location.provider", srcs: ["java/**/*.java"], api_packages: ["com.android.location.provider"], + metalava_enabled: false, } diff --git a/media/java/android/media/MediaPlayer2Impl.java b/media/java/android/media/MediaPlayer2Impl.java index 84d246f50ee3..77d9c7a75a59 100644 --- a/media/java/android/media/MediaPlayer2Impl.java +++ b/media/java/android/media/MediaPlayer2Impl.java @@ -36,8 +36,6 @@ import android.os.Handler; import android.os.HandlerThread; import android.os.Looper; import android.os.Message; -import android.os.Parcel; -import android.os.Parcelable; import android.os.PersistableBundle; import android.os.PowerManager; import android.os.Process; @@ -60,7 +58,6 @@ import com.android.internal.util.Preconditions; import dalvik.system.CloseGuard; import libcore.io.IoBridge; -import libcore.io.Streams; import java.io.ByteArrayOutputStream; import java.io.File; @@ -335,19 +332,14 @@ public final class MediaPlayer2Impl extends MediaPlayer2 { final String msg = "Cannot set AudioAttributes to null"; throw new IllegalArgumentException(msg); } - Parcel pattributes = Parcel.obtain(); - attributes.writeToParcel(pattributes, AudioAttributes.FLATTEN_TAGS); - setParameter(KEY_PARAMETER_AUDIO_ATTRIBUTES, pattributes); - pattributes.recycle(); + setParameter(KEY_PARAMETER_AUDIO_ATTRIBUTES, attributes); } }); } @Override public @NonNull AudioAttributes getAudioAttributes() { - Parcel pattributes = getParameter(KEY_PARAMETER_AUDIO_ATTRIBUTES); - AudioAttributes attributes = AudioAttributes.CREATOR.createFromParcel(pattributes); - pattributes.recycle(); + AudioAttributes attributes = (AudioAttributes) getParameter(KEY_PARAMETER_AUDIO_ATTRIBUTES); return attributes; } @@ -1588,9 +1580,9 @@ public final class MediaPlayer2Impl extends MediaPlayer2 { * @param value value of the parameter to be set. * @return true if the parameter is set successfully, false otherwise */ - private native boolean setParameter(int key, Parcel value); + private native boolean setParameter(int key, Object value); - private native Parcel getParameter(int key); + private native Object getParameter(int key); /** @@ -3681,7 +3673,7 @@ public final class MediaPlayer2Impl extends MediaPlayer2 { supportedSchemes = new UUID[supportedDRMsCount]; for (int i = 0; i < supportedDRMsCount; i++) { byte[] uuid = new byte[16]; - in.next().getBytesValue().copyTo(uuid, uuid.length); + in.next().getBytesValue().copyTo(uuid, 0); supportedSchemes[i] = bytesToUUID(uuid); @@ -3689,7 +3681,7 @@ public final class MediaPlayer2Impl extends MediaPlayer2 { supportedSchemes[i]); } - Log.v(TAG, "DrmInfoImpl() Parcel psshsize: " + pssh.length + + Log.v(TAG, "DrmInfoImpl() psshsize: " + pssh.length + " supportedDRMsCount: " + supportedDRMsCount); } @@ -3954,7 +3946,7 @@ public final class MediaPlayer2Impl extends MediaPlayer2 { connection.setReadTimeout(TIMEOUT_MS); connection.connect(); - response = Streams.readFully(connection.getInputStream()); + response = readInputStreamFully(connection.getInputStream()); Log.v(TAG, "HandleProvisioninig: Thread run: response " + response.length + " " + response); @@ -4034,6 +4026,29 @@ public final class MediaPlayer2Impl extends MediaPlayer2 { finished = true; } // run() + /** + * Returns a byte[] containing the remainder of 'in', closing it when done. + */ + private byte[] readInputStreamFully(InputStream in) throws IOException { + try { + return readInputStreamFullyNoClose(in); + } finally { + in.close(); + } + } + + /** + * Returns a byte[] containing the remainder of 'in'. + */ + private byte[] readInputStreamFullyNoClose(InputStream in) throws IOException { + ByteArrayOutputStream bytes = new ByteArrayOutputStream(); + byte[] buffer = new byte[1024]; + int count; + while ((count = in.read(buffer)) != -1) { + bytes.write(buffer, 0, count); + } + return bytes.toByteArray(); + } } // ProvisioningThread private int HandleProvisioninig(UUID uuid) { diff --git a/media/java/android/media/MediaScanner.java b/media/java/android/media/MediaScanner.java index 0ff2d8f6b6ec..c537945624fe 100644 --- a/media/java/android/media/MediaScanner.java +++ b/media/java/android/media/MediaScanner.java @@ -160,8 +160,9 @@ public class MediaScanner implements AutoCloseable { public static final String SCANNED_BUILD_PREFS_NAME = "MediaScanBuild"; public static final String LAST_INTERNAL_SCAN_FINGERPRINT = "lastScanFingerprint"; - private static final String SYSTEM_SOUNDS_DIR = "/system/media/audio"; - private static final String PRODUCT_SOUNDS_DIR = "/product/media/audio"; + private static final String SYSTEM_SOUNDS_DIR = Environment.getRootDirectory() + "/media/audio"; + private static final String OEM_SOUNDS_DIR = Environment.getOemDirectory() + "/media/audio"; + private static final String PRODUCT_SOUNDS_DIR = Environment.getProductDirectory() + "/media/audio"; private static String sLastInternalScanFingerprint; private static final String[] ID3_GENRES = { @@ -1193,6 +1194,9 @@ public class MediaScanner implements AutoCloseable { if (path.startsWith(SYSTEM_SOUNDS_DIR + ALARMS_DIR) || path.startsWith(SYSTEM_SOUNDS_DIR + RINGTONES_DIR) || path.startsWith(SYSTEM_SOUNDS_DIR + NOTIFICATIONS_DIR) + || path.startsWith(OEM_SOUNDS_DIR + ALARMS_DIR) + || path.startsWith(OEM_SOUNDS_DIR + RINGTONES_DIR) + || path.startsWith(OEM_SOUNDS_DIR + NOTIFICATIONS_DIR) || path.startsWith(PRODUCT_SOUNDS_DIR + ALARMS_DIR) || path.startsWith(PRODUCT_SOUNDS_DIR + RINGTONES_DIR) || path.startsWith(PRODUCT_SOUNDS_DIR + NOTIFICATIONS_DIR)) { diff --git a/media/jni/android_media_MediaPlayer2.cpp b/media/jni/android_media_MediaPlayer2.cpp index 1a844cc678c6..693a3d0b23df 100644 --- a/media/jni/android_media_MediaPlayer2.cpp +++ b/media/jni/android_media_MediaPlayer2.cpp @@ -1490,8 +1490,8 @@ static const JNINativeMethod gMethods[] = { {"_release", "()V", (void *)android_media_MediaPlayer2_release}, {"_reset", "()V", (void *)android_media_MediaPlayer2_reset}, {"_getAudioStreamType", "()I", (void *)android_media_MediaPlayer2_getAudioStreamType}, - {"setParameter", "(ILandroid/os/Parcel;)Z", (void *)android_media_MediaPlayer2_setParameter}, - {"getParameter", "(I)Landroid/os/Parcel;", (void *)android_media_MediaPlayer2_getParameter}, + {"setParameter", "(ILjava/lang/Object;)Z", (void *)android_media_MediaPlayer2_setParameter}, + {"getParameter", "(I)Ljava/lang/Object;", (void *)android_media_MediaPlayer2_getParameter}, {"setLooping", "(Z)V", (void *)android_media_MediaPlayer2_setLooping}, {"isLooping", "()Z", (void *)android_media_MediaPlayer2_isLooping}, {"_setVolume", "(FF)V", (void *)android_media_MediaPlayer2_setVolume}, diff --git a/media/lib/remotedisplay/Android.bp b/media/lib/remotedisplay/Android.bp index 1e9320d1414d..5f4b930f350e 100644 --- a/media/lib/remotedisplay/Android.bp +++ b/media/lib/remotedisplay/Android.bp @@ -14,22 +14,8 @@ // limitations under the License. // -droiddoc { - name: "com.android.media.remotedisplay.stubs-gen-docs", - srcs: [ - "java/**/*.java", - ], - args: " -hide 111 -hide 113 -hide 125 -hide 126 -hide 127 -hide 128 " + - " -stubpackages com.android.media.remotedisplay " + - " -nodocs ", - custom_template: "droiddoc-templates-sdk", - installable: false, -} - -java_library_static { - name: "com.android.media.remotedisplay.stubs", - srcs: [ - ":com.android.media.remotedisplay.stubs-gen-docs", - ], - sdk_version: "current", +java_sdk_library { + name: "com.android.media.remotedisplay", + srcs: ["java/**/*.java"], + api_packages: ["com.android.media.remotedisplay"], } diff --git a/media/lib/remotedisplay/Android.mk b/media/lib/remotedisplay/Android.mk deleted file mode 100644 index e88c0f1a8dc8..000000000000 --- a/media/lib/remotedisplay/Android.mk +++ /dev/null @@ -1,44 +0,0 @@ -# -# Copyright (C) 2013 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. -# -LOCAL_PATH := $(call my-dir) - -# the remotedisplay library -# ============================================================ -include $(CLEAR_VARS) - -LOCAL_MODULE:= com.android.media.remotedisplay -LOCAL_MODULE_TAGS := optional - -LOCAL_SRC_FILES := $(call all-java-files-under, java) - -include $(BUILD_JAVA_LIBRARY) - - -# ==== com.android.media.remotedisplay.xml lib def ======================== -include $(CLEAR_VARS) - -LOCAL_MODULE := com.android.media.remotedisplay.xml -LOCAL_MODULE_TAGS := optional - -LOCAL_MODULE_CLASS := ETC - -# This will install the file in /system/etc/permissions -# -LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)/permissions - -LOCAL_SRC_FILES := $(LOCAL_MODULE) - -include $(BUILD_PREBUILT) diff --git a/media/lib/remotedisplay/api/current.txt b/media/lib/remotedisplay/api/current.txt new file mode 100644 index 000000000000..e69de29bb2d1 --- /dev/null +++ b/media/lib/remotedisplay/api/current.txt diff --git a/media/lib/remotedisplay/api/removed.txt b/media/lib/remotedisplay/api/removed.txt new file mode 100644 index 000000000000..e69de29bb2d1 --- /dev/null +++ b/media/lib/remotedisplay/api/removed.txt diff --git a/media/lib/remotedisplay/api/system-current.txt b/media/lib/remotedisplay/api/system-current.txt new file mode 100644 index 000000000000..69bbd35fce59 --- /dev/null +++ b/media/lib/remotedisplay/api/system-current.txt @@ -0,0 +1,52 @@ +package com.android.media.remotedisplay { + + public class RemoteDisplay { + ctor public RemoteDisplay(java.lang.String, java.lang.String); + method public java.lang.String getDescription(); + method public java.lang.String getId(); + method public java.lang.String getName(); + method public int getPresentationDisplayId(); + method public int getStatus(); + method public int getVolume(); + method public int getVolumeHandling(); + method public int getVolumeMax(); + method public void setDescription(java.lang.String); + method public void setName(java.lang.String); + method public void setPresentationDisplayId(int); + method public void setStatus(int); + method public void setVolume(int); + method public void setVolumeHandling(int); + method public void setVolumeMax(int); + field public static final int PLAYBACK_VOLUME_FIXED = 0; // 0x0 + field public static final int PLAYBACK_VOLUME_VARIABLE = 1; // 0x1 + field public static final int STATUS_AVAILABLE = 2; // 0x2 + field public static final int STATUS_CONNECTED = 4; // 0x4 + field public static final int STATUS_CONNECTING = 3; // 0x3 + field public static final int STATUS_IN_USE = 1; // 0x1 + field public static final int STATUS_NOT_AVAILABLE = 0; // 0x0 + } + + public abstract class RemoteDisplayProvider { + ctor public RemoteDisplayProvider(android.content.Context); + method public void addDisplay(com.android.media.remotedisplay.RemoteDisplay); + method public com.android.media.remotedisplay.RemoteDisplay findRemoteDisplay(java.lang.String); + method public android.os.IBinder getBinder(); + method public final android.content.Context getContext(); + method public int getDiscoveryMode(); + method public java.util.Collection<com.android.media.remotedisplay.RemoteDisplay> getDisplays(); + method public android.app.PendingIntent getSettingsPendingIntent(); + method public void onAdjustVolume(com.android.media.remotedisplay.RemoteDisplay, int); + method public void onConnect(com.android.media.remotedisplay.RemoteDisplay); + method public void onDisconnect(com.android.media.remotedisplay.RemoteDisplay); + method public void onDiscoveryModeChanged(int); + method public void onSetVolume(com.android.media.remotedisplay.RemoteDisplay, int); + method public void removeDisplay(com.android.media.remotedisplay.RemoteDisplay); + method public void updateDisplay(com.android.media.remotedisplay.RemoteDisplay); + field public static final int DISCOVERY_MODE_ACTIVE = 2; // 0x2 + field public static final int DISCOVERY_MODE_NONE = 0; // 0x0 + field public static final int DISCOVERY_MODE_PASSIVE = 1; // 0x1 + field public static final java.lang.String SERVICE_INTERFACE = "com.android.media.remotedisplay.RemoteDisplayProvider"; + } + +} + diff --git a/media/lib/remotedisplay/api/system-removed.txt b/media/lib/remotedisplay/api/system-removed.txt new file mode 100644 index 000000000000..e69de29bb2d1 --- /dev/null +++ b/media/lib/remotedisplay/api/system-removed.txt diff --git a/media/lib/remotedisplay/api/test-current.txt b/media/lib/remotedisplay/api/test-current.txt new file mode 100644 index 000000000000..e69de29bb2d1 --- /dev/null +++ b/media/lib/remotedisplay/api/test-current.txt diff --git a/media/lib/remotedisplay/api/test-removed.txt b/media/lib/remotedisplay/api/test-removed.txt new file mode 100644 index 000000000000..e69de29bb2d1 --- /dev/null +++ b/media/lib/remotedisplay/api/test-removed.txt diff --git a/media/lib/remotedisplay/com.android.media.remotedisplay.xml b/media/lib/remotedisplay/com.android.media.remotedisplay.xml deleted file mode 100644 index 77a91d23e1d8..000000000000 --- a/media/lib/remotedisplay/com.android.media.remotedisplay.xml +++ /dev/null @@ -1,20 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 2013 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. ---> - -<permissions> - <library name="com.android.media.remotedisplay" - file="/system/framework/com.android.media.remotedisplay.jar" /> -</permissions> diff --git a/media/lib/remotedisplay/java/com/android/media/remotedisplay/RemoteDisplay.java b/media/lib/remotedisplay/java/com/android/media/remotedisplay/RemoteDisplay.java index dc9dd79eb2b1..8de414b45a4d 100644 --- a/media/lib/remotedisplay/java/com/android/media/remotedisplay/RemoteDisplay.java +++ b/media/lib/remotedisplay/java/com/android/media/remotedisplay/RemoteDisplay.java @@ -16,6 +16,7 @@ package com.android.media.remotedisplay; +import android.annotation.SystemApi; import android.media.RemoteDisplayState.RemoteDisplayInfo; import android.text.TextUtils; @@ -23,7 +24,10 @@ import java.util.Objects; /** * Represents a remote display that has been discovered. + * + * @hide */ +@SystemApi public class RemoteDisplay { private final RemoteDisplayInfo mMutableInfo; private RemoteDisplayInfo mImmutableInfo; diff --git a/media/lib/remotedisplay/java/com/android/media/remotedisplay/RemoteDisplayProvider.java b/media/lib/remotedisplay/java/com/android/media/remotedisplay/RemoteDisplayProvider.java index 4d3edb896eb5..7017e444d717 100644 --- a/media/lib/remotedisplay/java/com/android/media/remotedisplay/RemoteDisplayProvider.java +++ b/media/lib/remotedisplay/java/com/android/media/remotedisplay/RemoteDisplayProvider.java @@ -16,6 +16,7 @@ package com.android.media.remotedisplay; +import android.annotation.SystemApi; import android.app.PendingIntent; import android.app.Service; import android.content.Context; @@ -88,7 +89,10 @@ import java.util.Collection; * IMPORTANT: This class is effectively a public API for unbundled applications, and * must remain API stable. See README.txt in the root of this package for more information. * </p> + * + * @hide */ +@SystemApi public abstract class RemoteDisplayProvider { private static final int MSG_SET_CALLBACK = 1; private static final int MSG_SET_DISCOVERY_MODE = 2; diff --git a/media/lib/signer/Android.bp b/media/lib/signer/Android.bp index 3b2578754087..8c43683c2eec 100644 --- a/media/lib/signer/Android.bp +++ b/media/lib/signer/Android.bp @@ -18,4 +18,5 @@ java_sdk_library { name: "com.android.mediadrm.signer", srcs: ["java/**/*.java"], api_packages: ["com.android.mediadrm.signer"], + metalava_enabled: false, } diff --git a/native/android/system_fonts.cpp b/native/android/system_fonts.cpp index b95adad78f89..761a475a2773 100644 --- a/native/android/system_fonts.cpp +++ b/native/android/system_fonts.cpp @@ -43,6 +43,9 @@ using XmlDocUniquePtr = std::unique_ptr<xmlDoc, XmlDocDeleter>; struct ASystemFontIterator { XmlDocUniquePtr mXmlDoc; xmlNode* mFontNode; + + // The OEM customization XML. + XmlDocUniquePtr mCustomizationXmlDoc; }; struct ASystemFont { @@ -93,29 +96,30 @@ xmlNode* nextSibling(xmlNode* node, const xmlChar* tag) { return nullptr; } -void copyFont(ASystemFontIterator* ite, ASystemFont* out) { +void copyFont(const XmlDocUniquePtr& xmlDoc, xmlNode* fontNode, ASystemFont* out, + const std::string& pathPrefix) { const xmlChar* LOCALE_ATTR_NAME = BAD_CAST("lang"); XmlCharUniquePtr filePathStr( - xmlNodeListGetString(ite->mXmlDoc.get(), ite->mFontNode->xmlChildrenNode, 1)); - out->mFilePath = "/system/fonts/" + xmlTrim( + xmlNodeListGetString(xmlDoc.get(), fontNode->xmlChildrenNode, 1)); + out->mFilePath = pathPrefix + xmlTrim( std::string(filePathStr.get(), filePathStr.get() + xmlStrlen(filePathStr.get()))); const xmlChar* WEIGHT_ATTR_NAME = BAD_CAST("weight"); - XmlCharUniquePtr weightStr(xmlGetProp(ite->mFontNode, WEIGHT_ATTR_NAME)); + XmlCharUniquePtr weightStr(xmlGetProp(fontNode, WEIGHT_ATTR_NAME)); out->mWeight = weightStr ? strtol(reinterpret_cast<const char*>(weightStr.get()), nullptr, 10) : 400; const xmlChar* STYLE_ATTR_NAME = BAD_CAST("style"); const xmlChar* ITALIC_ATTR_VALUE = BAD_CAST("italic"); - XmlCharUniquePtr styleStr(xmlGetProp(ite->mFontNode, STYLE_ATTR_NAME)); + XmlCharUniquePtr styleStr(xmlGetProp(fontNode, STYLE_ATTR_NAME)); out->mItalic = styleStr ? xmlStrEqual(styleStr.get(), ITALIC_ATTR_VALUE) : false; const xmlChar* INDEX_ATTR_NAME = BAD_CAST("index"); - XmlCharUniquePtr indexStr(xmlGetProp(ite->mFontNode, INDEX_ATTR_NAME)); + XmlCharUniquePtr indexStr(xmlGetProp(fontNode, INDEX_ATTR_NAME)); out->mCollectionIndex = indexStr ? strtol(reinterpret_cast<const char*>(indexStr.get()), nullptr, 10) : 0; - XmlCharUniquePtr localeStr(xmlGetProp(ite->mXmlDoc->parent, LOCALE_ATTR_NAME)); + XmlCharUniquePtr localeStr(xmlGetProp(xmlDoc->parent, LOCALE_ATTR_NAME)); out->mLocale.reset( localeStr ? new std::string(reinterpret_cast<const char*>(localeStr.get())) : nullptr); @@ -123,7 +127,7 @@ void copyFont(ASystemFontIterator* ite, ASystemFont* out) { const xmlChar* STYLEVALUE_ATTR_NAME = BAD_CAST("stylevalue"); const xmlChar* AXIS_TAG = BAD_CAST("axis"); out->mAxes.clear(); - for (xmlNode* axis = firstElement(ite->mFontNode, AXIS_TAG); axis; + for (xmlNode* axis = firstElement(fontNode, AXIS_TAG); axis; axis = nextSibling(axis, AXIS_TAG)) { XmlCharUniquePtr tagStr(xmlGetProp(axis, TAG_ATTR_NAME)); if (!tagStr || xmlStrlen(tagStr.get()) != 4) { @@ -154,8 +158,8 @@ bool isFontFileAvailable(const std::string& filePath) { return S_ISREG(st.st_mode); } -xmlNode* findFirstFontNode(xmlDoc* doc) { - xmlNode* familySet = xmlDocGetRootElement(doc); +xmlNode* findFirstFontNode(const XmlDocUniquePtr& doc) { + xmlNode* familySet = xmlDocGetRootElement(doc.get()); if (familySet == nullptr) { return nullptr; } @@ -180,6 +184,7 @@ xmlNode* findFirstFontNode(xmlDoc* doc) { ASystemFontIterator* ASystemFontIterator_open() { std::unique_ptr<ASystemFontIterator> ite(new ASystemFontIterator()); ite->mXmlDoc.reset(xmlReadFile("/system/etc/fonts.xml", nullptr, 0)); + ite->mCustomizationXmlDoc.reset(xmlReadFile("/product/etc/fonts_customization.xml", nullptr, 0)); return ite.release(); } @@ -187,45 +192,62 @@ void ASystemFontIterator_close(ASystemFontIterator* ite) { delete ite; } -ASystemFont* ASystemFontIterator_next(ASystemFontIterator* ite) { - LOG_ALWAYS_FATAL_IF(ite == nullptr, "nullptr has passed as iterator argument"); - if (ite->mFontNode == nullptr) { - if (ite->mXmlDoc == nullptr) { +xmlNode* findNextFontNode(const XmlDocUniquePtr& xmlDoc, xmlNode* fontNode) { + if (fontNode == nullptr) { + if (!xmlDoc) { return nullptr; // Already at the end. } else { // First time to query font. - ite->mFontNode = findFirstFontNode(ite->mXmlDoc.get()); - if (ite->mFontNode == nullptr) { - ite->mXmlDoc.reset(); - return nullptr; // No font node found. - } - std::unique_ptr<ASystemFont> font = std::make_unique<ASystemFont>(); - copyFont(ite, font.get()); - return font.release(); + return findFirstFontNode(xmlDoc); } } else { - xmlNode* nextNode = nextSibling(ite->mFontNode, FONT_TAG); + xmlNode* nextNode = nextSibling(fontNode, FONT_TAG); while (nextNode == nullptr) { - xmlNode* family = nextSibling(ite->mFontNode->parent, FAMILY_TAG); + xmlNode* family = nextSibling(fontNode->parent, FAMILY_TAG); if (family == nullptr) { break; } nextNode = firstElement(family, FONT_TAG); } - ite->mFontNode = nextNode; - if (nextNode == nullptr) { + return nextNode; + } +} + +ASystemFont* ASystemFontIterator_next(ASystemFontIterator* ite) { + LOG_ALWAYS_FATAL_IF(ite == nullptr, "nullptr has passed as iterator argument"); + if (ite->mXmlDoc) { + ite->mFontNode = findNextFontNode(ite->mXmlDoc, ite->mFontNode); + if (ite->mFontNode == nullptr) { + // Reached end of the XML file. Continue OEM customization. ite->mXmlDoc.reset(); - return nullptr; + ite->mFontNode = nullptr; + } else { + std::unique_ptr<ASystemFont> font = std::make_unique<ASystemFont>(); + copyFont(ite->mXmlDoc, ite->mFontNode, font.get(), "/system/fonts/"); + if (!isFontFileAvailable(font->mFilePath)) { + return ASystemFontIterator_next(ite); + } + return font.release(); } - - std::unique_ptr<ASystemFont> font = std::make_unique<ASystemFont>(); - copyFont(ite, font.get()); - if (!isFontFileAvailable(font->mFilePath)) { - // fonts.xml intentionally contains missing font configuration. Skip it. - return ASystemFontIterator_next(ite); + } + if (ite->mCustomizationXmlDoc) { + // TODO: Filter only customizationType="new-named-family" + ite->mFontNode = findNextFontNode(ite->mCustomizationXmlDoc, ite->mFontNode); + if (ite->mFontNode == nullptr) { + // Reached end of the XML file. Finishing + ite->mCustomizationXmlDoc.reset(); + ite->mFontNode = nullptr; + return nullptr; + } else { + std::unique_ptr<ASystemFont> font = std::make_unique<ASystemFont>(); + copyFont(ite->mCustomizationXmlDoc, ite->mFontNode, font.get(), "/product/fonts/"); + if (!isFontFileAvailable(font->mFilePath)) { + return ASystemFontIterator_next(ite); + } + return font.release(); } - return font.release(); } + return nullptr; } void ASystemFont_close(ASystemFont* font) { diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java b/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java index 580308a4cffd..8c29a2520390 100644 --- a/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java +++ b/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java @@ -430,9 +430,14 @@ public class PackageInstallerActivity extends AlertActivity { // Check for unknown sources restriction final int unknownSourcesRestrictionSource = mUserManager.getUserRestrictionSource( UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES, Process.myUserHandle()); - if ((unknownSourcesRestrictionSource & UserManager.RESTRICTION_SOURCE_SYSTEM) != 0) { + final int unknownSourcesGlobalRestrictionSource = mUserManager.getUserRestrictionSource( + UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES_GLOBALLY, Process.myUserHandle()); + final int systemRestriction = UserManager.RESTRICTION_SOURCE_SYSTEM + & (unknownSourcesRestrictionSource | unknownSourcesGlobalRestrictionSource); + if (systemRestriction != 0) { showDialogInner(DLG_UNKNOWN_SOURCES_RESTRICTED_FOR_USER); - } else if (unknownSourcesRestrictionSource != UserManager.RESTRICTION_NOT_SET) { + } else if (unknownSourcesRestrictionSource != UserManager.RESTRICTION_NOT_SET + || unknownSourcesGlobalRestrictionSource != UserManager.RESTRICTION_NOT_SET) { startActivity(new Intent(Settings.ACTION_SHOW_ADMIN_SUPPORT_DETAILS)); finish(); } else { diff --git a/packages/SettingsLib/Android.bp b/packages/SettingsLib/Android.bp index ee4c95445bff..89438e555b0d 100644 --- a/packages/SettingsLib/Android.bp +++ b/packages/SettingsLib/Android.bp @@ -15,6 +15,7 @@ android_library { "SettingsLibHelpUtils", "SettingsLibRestrictedLockUtils", "SettingsLibAppPreference", + "SettingsLibSearchWidget", ], // ANDROIDMK TRANSLATION ERROR: unsupported assignment to LOCAL_SHARED_JAVA_LIBRARIES diff --git a/packages/SettingsLib/HelpUtils/res/values-nl/strings.xml b/packages/SettingsLib/HelpUtils/res/values-nl/strings.xml index a034d297bac8..2f576e62202e 100644 --- a/packages/SettingsLib/HelpUtils/res/values-nl/strings.xml +++ b/packages/SettingsLib/HelpUtils/res/values-nl/strings.xml @@ -17,5 +17,5 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> - <string name="help_feedback_label" msgid="4550436169116444686">"Help en feedback"</string> + <string name="help_feedback_label" msgid="4550436169116444686">"Hulp en feedback"</string> </resources> diff --git a/packages/SettingsLib/SearchWidget/Android.bp b/packages/SettingsLib/SearchWidget/Android.bp new file mode 100644 index 000000000000..7541ca456138 --- /dev/null +++ b/packages/SettingsLib/SearchWidget/Android.bp @@ -0,0 +1,8 @@ +android_library { + name: "SettingsLibSearchWidget", + + srcs: ["src/**/*.java"], + resource_dirs: ["res"], + sdk_version: "system_current", + min_sdk_version: "21", +} diff --git a/packages/SettingsLib/SearchWidget/AndroidManifest.xml b/packages/SettingsLib/SearchWidget/AndroidManifest.xml new file mode 100644 index 000000000000..b86544ec68c1 --- /dev/null +++ b/packages/SettingsLib/SearchWidget/AndroidManifest.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + 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. + --> + +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.settingslib.search"> + + <uses-sdk android:minSdkVersion="21" /> + +</manifest> diff --git a/packages/SettingsLib/SearchWidget/res/drawable/ic_search_24dp.xml b/packages/SettingsLib/SearchWidget/res/drawable/ic_search_24dp.xml new file mode 100644 index 000000000000..7e65848de189 --- /dev/null +++ b/packages/SettingsLib/SearchWidget/res/drawable/ic_search_24dp.xml @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + 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. + --> + +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24" + android:tint="?android:attr/colorControlNormal"> + <path + android:fillColor="#FF000000" + android:pathData="M20.49,19l-5.73,-5.73C15.53,12.2 16,10.91 16,9.5C16,5.91 13.09,3 9.5,3S3,5.91 3,9.5C3,13.09 5.91,16 9.5,16c1.41,0 2.7,-0.47 3.77,-1.24L19,20.49L20.49,19zM5,9.5C5,7.01 7.01,5 9.5,5S14,7.01 14,9.5S11.99,14 9.5,14S5,11.99 5,9.5z"/> +</vector> diff --git a/packages/SettingsLib/SearchWidget/res/values/strings.xml b/packages/SettingsLib/SearchWidget/res/values/strings.xml new file mode 100644 index 000000000000..0b12810ffc38 --- /dev/null +++ b/packages/SettingsLib/SearchWidget/res/values/strings.xml @@ -0,0 +1,20 @@ +<!-- + 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. + --> + +<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <!-- Text used as a search hint into the search box [CHAR_LIMIT=60]--> + <string name="search_menu">Search settings</string> +</resources> diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml index 332ced66ef77..508adbd2a121 100644 --- a/packages/SettingsLib/res/values/strings.xml +++ b/packages/SettingsLib/res/values/strings.xml @@ -1116,4 +1116,6 @@ <!-- time label for event have that happened very recently [CHAR LIMIT=60] --> <string name="time_unit_just_now">Just now</string> - </resources> + <!-- The notice header of Third-party licenses. not translatable --> + <string name="notice_header" translatable="false"></string> +</resources> diff --git a/packages/SettingsLib/search/Android.mk b/packages/SettingsLib/search/Android.mk index cb1989157db8..14f96269c54e 100644 --- a/packages/SettingsLib/search/Android.mk +++ b/packages/SettingsLib/search/Android.mk @@ -5,6 +5,9 @@ LOCAL_MODULE = SettingsLib-search LOCAL_SRC_FILES := $(call all-java-files-under, src) +LOCAL_RESOURCE_DIR := \ + $(LOCAL_PATH)/main/res + include $(BUILD_STATIC_JAVA_LIBRARY) diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java index 0c29f431ef3f..9653972414ee 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LocalBluetoothProfileManager.java @@ -30,6 +30,7 @@ import android.bluetooth.BluetoothMapClient; import android.bluetooth.BluetoothPan; import android.bluetooth.BluetoothPbap; import android.bluetooth.BluetoothPbapClient; +import android.bluetooth.BluetoothSap; import android.bluetooth.BluetoothProfile; import android.bluetooth.BluetoothUuid; import android.content.Context; @@ -98,6 +99,7 @@ public class LocalBluetoothProfileManager { private PbapClientProfile mPbapClientProfile; private PbapServerProfile mPbapProfile; private HearingAidProfile mHearingAidProfile; + private SapProfile mSapProfile; /** * Mapping from profile name, e.g. "HEADSET" to profile object. @@ -210,6 +212,13 @@ public class LocalBluetoothProfileManager { addProfile(mPbapClientProfile, PbapClientProfile.NAME, BluetoothPbapClient.ACTION_CONNECTION_STATE_CHANGED); } + if (mSapProfile == null && supportedList.contains(BluetoothProfile.SAP)) { + if (DEBUG) { + Log.d(TAG, "Adding local SAP profile"); + } + mSapProfile = new SapProfile(mContext, mDeviceManager, this); + addProfile(mSapProfile, SapProfile.NAME, BluetoothSap.ACTION_CONNECTION_STATE_CHANGED); + } mEventManager.registerProfileIntentReceiver(); } @@ -550,6 +559,11 @@ public class LocalBluetoothProfileManager { removedProfiles.remove(mHearingAidProfile); } + if (mSapProfile != null && BluetoothUuid.isUuidPresent(uuids, BluetoothUuid.SAP)) { + profiles.add(mSapProfile); + removedProfiles.remove(mSapProfile); + } + if (DEBUG) { Log.d(TAG,"New Profiles" + profiles.toString()); } diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/SapProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/SapProfile.java index 9a6f104fadd5..b4acc4810faf 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/SapProfile.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/SapProfile.java @@ -36,12 +36,10 @@ import java.util.List; */ final class SapProfile implements LocalBluetoothProfile { private static final String TAG = "SapProfile"; - private static boolean V = true; private BluetoothSap mService; private boolean mIsProfileReady; - private final LocalBluetoothAdapter mLocalAdapter; private final CachedBluetoothDeviceManager mDeviceManager; private final LocalBluetoothProfileManager mProfileManager; @@ -59,7 +57,7 @@ final class SapProfile implements LocalBluetoothProfile { implements BluetoothProfile.ServiceListener { public void onServiceConnected(int profile, BluetoothProfile proxy) { - if (V) Log.d(TAG,"Bluetooth service connected"); + Log.d(TAG, "Bluetooth service connected, profile:" + profile); mService = (BluetoothSap) proxy; // We just bound to the service, so refresh the UI for any connected SAP devices. List<BluetoothDevice> deviceList = mService.getConnectedDevices(); @@ -81,7 +79,7 @@ final class SapProfile implements LocalBluetoothProfile { } public void onServiceDisconnected(int profile) { - if (V) Log.d(TAG,"Bluetooth service disconnected"); + Log.d(TAG, "Bluetooth service disconnected, profile:" + profile); mProfileManager.callServiceDisconnectedListeners(); mIsProfileReady=false; } @@ -96,13 +94,11 @@ final class SapProfile implements LocalBluetoothProfile { return BluetoothProfile.SAP; } - SapProfile(Context context, LocalBluetoothAdapter adapter, - CachedBluetoothDeviceManager deviceManager, + SapProfile(Context context, CachedBluetoothDeviceManager deviceManager, LocalBluetoothProfileManager profileManager) { - mLocalAdapter = adapter; mDeviceManager = deviceManager; mProfileManager = profileManager; - mLocalAdapter.getProfileProxy(context, new SapServiceListener(), + BluetoothAdapter.getDefaultAdapter().getProfileProxy(context, new SapServiceListener(), BluetoothProfile.SAP); } @@ -115,50 +111,47 @@ final class SapProfile implements LocalBluetoothProfile { } public boolean connect(BluetoothDevice device) { - if (mService == null) return false; - List<BluetoothDevice> sinks = mService.getConnectedDevices(); - if (sinks != null) { - for (BluetoothDevice sink : sinks) { - mService.disconnect(sink); - } + if (mService == null) { + return false; } return mService.connect(device); } public boolean disconnect(BluetoothDevice device) { - if (mService == null) return false; - List<BluetoothDevice> deviceList = mService.getConnectedDevices(); - if (!deviceList.isEmpty() && deviceList.get(0).equals(device)) { - if (mService.getPriority(device) > BluetoothProfile.PRIORITY_ON) { - mService.setPriority(device, BluetoothProfile.PRIORITY_ON); - } - return mService.disconnect(device); - } else { + if (mService == null) { return false; } + if (mService.getPriority(device) > BluetoothProfile.PRIORITY_ON) { + mService.setPriority(device, BluetoothProfile.PRIORITY_ON); + } + return mService.disconnect(device); } public int getConnectionStatus(BluetoothDevice device) { - if (mService == null) return BluetoothProfile.STATE_DISCONNECTED; - List<BluetoothDevice> deviceList = mService.getConnectedDevices(); - - return !deviceList.isEmpty() && deviceList.get(0).equals(device) - ? mService.getConnectionState(device) - : BluetoothProfile.STATE_DISCONNECTED; + if (mService == null) { + return BluetoothProfile.STATE_DISCONNECTED; + } + return mService.getConnectionState(device); } public boolean isPreferred(BluetoothDevice device) { - if (mService == null) return false; + if (mService == null) { + return false; + } return mService.getPriority(device) > BluetoothProfile.PRIORITY_OFF; } public int getPreferred(BluetoothDevice device) { - if (mService == null) return BluetoothProfile.PRIORITY_OFF; + if (mService == null) { + return BluetoothProfile.PRIORITY_OFF; + } return mService.getPriority(device); } public void setPreferred(BluetoothDevice device, boolean preferred) { - if (mService == null) return; + if (mService == null) { + return; + } if (preferred) { if (mService.getPriority(device) < BluetoothProfile.PRIORITY_ON) { mService.setPriority(device, BluetoothProfile.PRIORITY_ON); @@ -169,7 +162,9 @@ final class SapProfile implements LocalBluetoothProfile { } public List<BluetoothDevice> getConnectedDevices() { - if (mService == null) return new ArrayList<BluetoothDevice>(0); + if (mService == null) { + return new ArrayList<BluetoothDevice>(0); + } return mService.getDevicesMatchingConnectionStates( new int[] {BluetoothProfile.STATE_CONNECTED, BluetoothProfile.STATE_CONNECTING, @@ -207,11 +202,11 @@ final class SapProfile implements LocalBluetoothProfile { } protected void finalize() { - if (V) Log.d(TAG, "finalize()"); + Log.d(TAG, "finalize()"); if (mService != null) { try { BluetoothAdapter.getDefaultAdapter().closeProfileProxy(BluetoothProfile.SAP, - mService); + mService); mService = null; }catch (Throwable t) { Log.w(TAG, "Error cleaning up SAP proxy", t); diff --git a/packages/SettingsLib/src/com/android/settingslib/license/LicenseHtmlGeneratorFromXml.java b/packages/SettingsLib/src/com/android/settingslib/license/LicenseHtmlGeneratorFromXml.java index 42306f6d46d0..9db4a35c1d78 100644 --- a/packages/SettingsLib/src/com/android/settingslib/license/LicenseHtmlGeneratorFromXml.java +++ b/packages/SettingsLib/src/com/android/settingslib/license/LicenseHtmlGeneratorFromXml.java @@ -46,7 +46,7 @@ import java.util.zip.GZIPInputStream; * TODO: Remove duplicate codes once backward support ends. */ class LicenseHtmlGeneratorFromXml { - private static final String TAG = "LicenseHtmlGeneratorFromXml"; + private static final String TAG = "LicenseGeneratorFromXml"; private static final String TAG_ROOT = "licenses"; private static final String TAG_FILE_NAME = "file-name"; @@ -107,12 +107,13 @@ class LicenseHtmlGeneratorFromXml { mXmlFiles = xmlFiles; } - public static boolean generateHtml(List<File> xmlFiles, File outputFile) { + public static boolean generateHtml(List<File> xmlFiles, File outputFile, + String noticeHeader) { LicenseHtmlGeneratorFromXml genertor = new LicenseHtmlGeneratorFromXml(xmlFiles); - return genertor.generateHtml(outputFile); + return genertor.generateHtml(outputFile, noticeHeader); } - private boolean generateHtml(File outputFile) { + private boolean generateHtml(File outputFile, String noticeHeader) { for (File xmlFile : mXmlFiles) { parse(xmlFile); } @@ -125,7 +126,8 @@ class LicenseHtmlGeneratorFromXml { try { writer = new PrintWriter(outputFile); - generateHtml(mFileNameToContentIdMap, mContentIdToFileContentMap, writer); + generateHtml(mFileNameToContentIdMap, mContentIdToFileContentMap, writer, + noticeHeader); writer.flush(); writer.close(); @@ -239,13 +241,18 @@ class LicenseHtmlGeneratorFromXml { @VisibleForTesting static void generateHtml(Map<String, String> fileNameToContentIdMap, - Map<String, String> contentIdToFileContentMap, PrintWriter writer) { + Map<String, String> contentIdToFileContentMap, PrintWriter writer, + String noticeHeader) { List<String> fileNameList = new ArrayList(); fileNameList.addAll(fileNameToContentIdMap.keySet()); Collections.sort(fileNameList); writer.println(HTML_HEAD_STRING); + if (!TextUtils.isEmpty(noticeHeader)) { + writer.println(noticeHeader); + } + int count = 0; Map<String, Integer> contentIdToOrderMap = new HashMap(); List<ContentIdAndFileNames> contentIdAndFileNamesList = new ArrayList(); diff --git a/packages/SettingsLib/src/com/android/settingslib/license/LicenseHtmlLoader.java b/packages/SettingsLib/src/com/android/settingslib/license/LicenseHtmlLoader.java index 393006940740..78e807cf1a1c 100644 --- a/packages/SettingsLib/src/com/android/settingslib/license/LicenseHtmlLoader.java +++ b/packages/SettingsLib/src/com/android/settingslib/license/LicenseHtmlLoader.java @@ -60,7 +60,7 @@ public class LicenseHtmlLoader extends AsyncLoader<File> { File cachedHtmlFile = getCachedHtmlFile(mContext); if (!isCachedHtmlFileOutdated(xmlFiles, cachedHtmlFile) - || generateHtmlFile(xmlFiles, cachedHtmlFile)) { + || generateHtmlFile(mContext, xmlFiles, cachedHtmlFile)) { return cachedHtmlFile; } diff --git a/packages/SettingsLib/src/com/android/settingslib/license/LicenseHtmlLoaderCompat.java b/packages/SettingsLib/src/com/android/settingslib/license/LicenseHtmlLoaderCompat.java index 360c19c2b795..ca6248505dc0 100644 --- a/packages/SettingsLib/src/com/android/settingslib/license/LicenseHtmlLoaderCompat.java +++ b/packages/SettingsLib/src/com/android/settingslib/license/LicenseHtmlLoaderCompat.java @@ -19,6 +19,7 @@ package com.android.settingslib.license; import android.content.Context; import android.util.Log; +import com.android.settingslib.R; import com.android.settingslib.utils.AsyncLoaderCompat; import java.io.File; @@ -65,7 +66,7 @@ public class LicenseHtmlLoaderCompat extends AsyncLoaderCompat<File> { File cachedHtmlFile = getCachedHtmlFile(mContext); if (!isCachedHtmlFileOutdated(xmlFiles, cachedHtmlFile) - || generateHtmlFile(xmlFiles, cachedHtmlFile)) { + || generateHtmlFile(mContext, xmlFiles, cachedHtmlFile)) { return cachedHtmlFile; } @@ -101,7 +102,8 @@ public class LicenseHtmlLoaderCompat extends AsyncLoaderCompat<File> { return outdated; } - static boolean generateHtmlFile(List<File> xmlFiles, File htmlFile) { - return LicenseHtmlGeneratorFromXml.generateHtml(xmlFiles, htmlFile); + static boolean generateHtmlFile(Context context, List<File> xmlFiles, File htmlFile) { + return LicenseHtmlGeneratorFromXml.generateHtml(xmlFiles, htmlFile, + context.getString(R.string.notice_header)); } } diff --git a/packages/SettingsLib/src/com/android/settingslib/net/ChartDataLoaderCompat.java b/packages/SettingsLib/src/com/android/settingslib/net/ChartDataLoaderCompat.java index 74bd97f40ff7..e9c523881373 100644 --- a/packages/SettingsLib/src/com/android/settingslib/net/ChartDataLoaderCompat.java +++ b/packages/SettingsLib/src/com/android/settingslib/net/ChartDataLoaderCompat.java @@ -37,7 +37,8 @@ import com.android.settingslib.AppItem; /** * Loader for historical chart data for both network and UID details. * - * Deprecated in favor of {@link NetworkCycleDataLoader} + * Deprecated in favor of {@link NetworkCycleChartDataLoader} and + * {@link NetworkCycleDataForUidLoader} * * @deprecated */ diff --git a/packages/SettingsLib/src/com/android/settingslib/net/NetworkCycleChartData.java b/packages/SettingsLib/src/com/android/settingslib/net/NetworkCycleChartData.java new file mode 100644 index 000000000000..9b3ff8b2e165 --- /dev/null +++ b/packages/SettingsLib/src/com/android/settingslib/net/NetworkCycleChartData.java @@ -0,0 +1,56 @@ +/* + * 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.settingslib.net; + +import java.util.List; +import java.util.concurrent.TimeUnit; + +/** + * Usage data in a billing cycle with bucketized data for plotting the usage chart. + */ +public class NetworkCycleChartData extends NetworkCycleData { + public static final long BUCKET_DURATION_MS = TimeUnit.DAYS.toMillis(1); + + private List<NetworkCycleData> mUsageBuckets; + + private NetworkCycleChartData() { + } + + public List<NetworkCycleData> getUsageBuckets() { + return mUsageBuckets; + } + + public static class Builder extends NetworkCycleData.Builder { + private NetworkCycleChartData mObject = new NetworkCycleChartData(); + + public Builder setUsageBuckets(List<NetworkCycleData> buckets) { + getObject().mUsageBuckets = buckets; + return this; + } + + @Override + protected NetworkCycleChartData getObject() { + return mObject; + } + + @Override + public NetworkCycleChartData build() { + return getObject(); + } + } + +} diff --git a/packages/SettingsLib/src/com/android/settingslib/net/NetworkCycleChartDataLoader.java b/packages/SettingsLib/src/com/android/settingslib/net/NetworkCycleChartDataLoader.java new file mode 100644 index 000000000000..7ae3398d42ea --- /dev/null +++ b/packages/SettingsLib/src/com/android/settingslib/net/NetworkCycleChartDataLoader.java @@ -0,0 +1,106 @@ +/* + * 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.settingslib.net; + +import android.app.usage.NetworkStats; +import android.content.Context; +import android.os.RemoteException; +import android.util.Log; + +import java.util.ArrayList; +import java.util.List; + +/** + * Loader for network data usage history. It returns a list of usage data per billing cycle with + * bucketized usages. + */ +public class NetworkCycleChartDataLoader + extends NetworkCycleDataLoader<List<NetworkCycleChartData>> { + + private static final String TAG = "NetworkCycleChartLoader"; + + private final List<NetworkCycleChartData> mData; + + private NetworkCycleChartDataLoader(Builder builder) { + super(builder); + mData = new ArrayList<NetworkCycleChartData>(); + } + + @Override + void recordUsage(long start, long end) { + try { + final NetworkStats stats = mNetworkStatsManager.querySummary( + mNetworkType, mSubId, start, end); + final long total = getTotalUsage(stats); + if (total > 0L) { + final NetworkCycleChartData.Builder builder = new NetworkCycleChartData.Builder(); + builder.setUsageBuckets(getUsageBuckets(start, end)) + .setStartTime(start) + .setEndTime(end) + .setTotalUsage(total); + mData.add(builder.build()); + } + } catch (RemoteException e) { + Log.e(TAG, "Exception querying network detail.", e); + } + } + + @Override + List<NetworkCycleChartData> getCycleUsage() { + return mData; + } + + public static Builder<?> builder(Context context) { + return new Builder<NetworkCycleChartDataLoader>(context) { + @Override + public NetworkCycleChartDataLoader build() { + return new NetworkCycleChartDataLoader(this); + } + }; + } + + private List<NetworkCycleData> getUsageBuckets(long start, long end) { + final List<NetworkCycleData> data = new ArrayList<>(); + long bucketStart = start; + long bucketEnd = start + NetworkCycleChartData.BUCKET_DURATION_MS; + while (bucketEnd <= end) { + long usage = 0L; + try { + final NetworkStats stats = mNetworkStatsManager.querySummary( + mNetworkType, mSubId, bucketStart, bucketEnd); + usage = getTotalUsage(stats); + } catch (RemoteException e) { + Log.e(TAG, "Exception querying network detail.", e); + } + data.add(new NetworkCycleData.Builder() + .setStartTime(bucketStart).setEndTime(bucketEnd).setTotalUsage(usage).build()); + bucketStart = bucketEnd; + bucketEnd += NetworkCycleChartData.BUCKET_DURATION_MS; + } + return data; + } + + public static abstract class Builder<T extends NetworkCycleChartDataLoader> + extends NetworkCycleDataLoader.Builder<T> { + + public Builder(Context context) { + super(context); + } + + } + +} diff --git a/packages/SettingsLib/src/com/android/settingslib/net/NetworkCycleData.java b/packages/SettingsLib/src/com/android/settingslib/net/NetworkCycleData.java index 2d8c0de42ba4..26c65a2c4a48 100644 --- a/packages/SettingsLib/src/com/android/settingslib/net/NetworkCycleData.java +++ b/packages/SettingsLib/src/com/android/settingslib/net/NetworkCycleData.java @@ -16,54 +16,55 @@ package com.android.settingslib.net; -import java.util.List; -import java.util.concurrent.TimeUnit; - /** - * Data structure representing usage data in a billing cycle. + * Base data structure representing usage data in a billing cycle. */ public class NetworkCycleData { - public static final long BUCKET_DURATION_MS = TimeUnit.DAYS.toMillis(1); - public long startTime; - public long endTime; - public long totalUsage; - public List<NetworkCycleData> usageBuckets; - private NetworkCycleData(Builder builder) { - startTime = builder.mStart; - endTime = builder.mEnd; - totalUsage = builder.mTotalUsage; - usageBuckets = builder.mUsageBuckets; + private long mStartTime; + private long mEndTime; + private long mTotalUsage; + + protected NetworkCycleData() { + } + + public long getStartTime() { + return mStartTime; + } + + public long getEndTime() { + return mEndTime; + } + + public long getTotalUsage() { + return mTotalUsage; } public static class Builder { - private long mStart; - private long mEnd; - private long mTotalUsage; - private List<NetworkCycleData> mUsageBuckets; + + private NetworkCycleData mObject = new NetworkCycleData(); public Builder setStartTime(long start) { - mStart = start; + getObject().mStartTime = start; return this; } public Builder setEndTime(long end) { - mEnd = end; + getObject().mEndTime = end; return this; } public Builder setTotalUsage(long total) { - mTotalUsage = total; + getObject().mTotalUsage = total; return this; } - public Builder setUsageBuckets(List<NetworkCycleData> buckets) { - mUsageBuckets = buckets; - return this; + protected NetworkCycleData getObject() { + return mObject; } public NetworkCycleData build() { - return new NetworkCycleData(this); + return getObject(); } } } diff --git a/packages/SettingsLib/src/com/android/settingslib/net/NetworkCycleDataForUid.java b/packages/SettingsLib/src/com/android/settingslib/net/NetworkCycleDataForUid.java new file mode 100644 index 000000000000..9d13717bbbcc --- /dev/null +++ b/packages/SettingsLib/src/com/android/settingslib/net/NetworkCycleDataForUid.java @@ -0,0 +1,65 @@ +/* + * 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.settingslib.net; + +import java.util.concurrent.TimeUnit; + +/** + * Usage data in a billing cycle for a specific Uid. + */ +public class NetworkCycleDataForUid extends NetworkCycleData { + + private long mBackgroudUsage; + private long mForegroudUsage; + + private NetworkCycleDataForUid() { + } + + public long getBackgroudUsage() { + return mBackgroudUsage; + } + + public long getForegroudUsage() { + return mForegroudUsage; + } + + public static class Builder extends NetworkCycleData.Builder { + + private NetworkCycleDataForUid mObject = new NetworkCycleDataForUid(); + + public Builder setBackgroundUsage(long backgroundUsage) { + getObject().mBackgroudUsage = backgroundUsage; + return this; + } + + public Builder setForegroundUsage(long foregroundUsage) { + getObject().mForegroudUsage = foregroundUsage; + return this; + } + + @Override + public NetworkCycleDataForUid getObject() { + return mObject; + } + + @Override + public NetworkCycleDataForUid build() { + return getObject(); + } + } + +} diff --git a/packages/SettingsLib/src/com/android/settingslib/net/NetworkCycleDataForUidLoader.java b/packages/SettingsLib/src/com/android/settingslib/net/NetworkCycleDataForUidLoader.java new file mode 100644 index 000000000000..cc970b93f601 --- /dev/null +++ b/packages/SettingsLib/src/com/android/settingslib/net/NetworkCycleDataForUidLoader.java @@ -0,0 +1,112 @@ +/* + * 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.settingslib.net; + +import static android.app.usage.NetworkStats.Bucket.STATE_FOREGROUND; +import static android.net.NetworkStats.TAG_NONE; + +import android.app.usage.NetworkStats; +import android.content.Context; +import android.util.Log; + +import java.util.ArrayList; +import java.util.List; + +/** + * Loader for network data usage history. It returns a list of usage data per billing cycle for a + * specific Uid. + */ +public class NetworkCycleDataForUidLoader extends + NetworkCycleDataLoader<List<NetworkCycleDataForUid>> { + private static final String TAG = "NetworkDataForUidLoader"; + + private final List<NetworkCycleDataForUid> mData; + private final int mUid; + private final boolean mRetrieveDetail; + + private NetworkCycleDataForUidLoader(Builder builder) { + super(builder); + mUid = builder.mUid; + mRetrieveDetail = builder.mRetrieveDetail; + mData = new ArrayList<NetworkCycleDataForUid>(); + } + + @Override + void recordUsage(long start, long end) { + try { + final NetworkStats stats = mNetworkStatsManager.queryDetailsForUid( + mNetworkType, mSubId, start, end, mUid); + final long total = getTotalUsage(stats); + if (total > 0L) { + final NetworkCycleDataForUid.Builder builder = new NetworkCycleDataForUid.Builder(); + builder.setStartTime(start) + .setEndTime(end) + .setTotalUsage(total); + if (mRetrieveDetail) { + final long foreground = getForegroundUsage(start, end); + builder.setBackgroundUsage(total - foreground) + .setForegroundUsage(foreground); + } + mData.add(builder.build()); + } + } catch (Exception e) { + Log.e(TAG, "Exception querying network detail.", e); + } + } + + @Override + List<NetworkCycleDataForUid> getCycleUsage() { + return mData; + } + + public static Builder<?> builder(Context context) { + return new Builder<NetworkCycleDataForUidLoader>(context) { + @Override + public NetworkCycleDataForUidLoader build() { + return new NetworkCycleDataForUidLoader(this); + } + }; + } + + private long getForegroundUsage(long start, long end) { + final NetworkStats stats = mNetworkStatsManager.queryDetailsForUidTagState( + mNetworkType, mSubId, start, end, mUid, TAG_NONE, STATE_FOREGROUND); + return getTotalUsage(stats); + } + + public static abstract class Builder<T extends NetworkCycleDataForUidLoader> + extends NetworkCycleDataLoader.Builder<T> { + + private int mUid; + private boolean mRetrieveDetail = true; + + public Builder(Context context) { + super(context); + } + + public Builder<T> setUid(int uid) { + mUid = uid; + return this; + } + + public Builder<T> setRetrieveDetail(boolean retrieveDetail) { + mRetrieveDetail = retrieveDetail; + return this; + } + } + +} diff --git a/packages/SettingsLib/src/com/android/settingslib/net/NetworkCycleDataLoader.java b/packages/SettingsLib/src/com/android/settingslib/net/NetworkCycleDataLoader.java index 80e13563d74b..b1c2c3a2d2e5 100644 --- a/packages/SettingsLib/src/com/android/settingslib/net/NetworkCycleDataLoader.java +++ b/packages/SettingsLib/src/com/android/settingslib/net/NetworkCycleDataLoader.java @@ -22,6 +22,7 @@ import static android.net.NetworkStatsHistory.FIELD_TX_BYTES; import android.app.usage.NetworkStats; import android.app.usage.NetworkStatsManager; import android.content.Context; +import android.net.ConnectivityManager; import android.net.INetworkStatsService; import android.net.INetworkStatsSession; import android.net.NetworkPolicy; @@ -32,34 +33,31 @@ import android.net.TrafficStats; import android.os.RemoteException; import android.os.ServiceManager; import android.text.format.DateUtils; -import android.util.Log; import android.util.Pair; +import com.android.settingslib.NetworkPolicyEditor; + import java.time.ZonedDateTime; -import java.util.ArrayList; import java.util.Iterator; -import java.util.List; -import androidx.annotation.NonNull; import androidx.annotation.VisibleForTesting; import androidx.loader.content.AsyncTaskLoader; /** * Loader for network data usage history. It returns a list of usage data per billing cycle. */ -public class NetworkCycleDataLoader extends AsyncTaskLoader<List<NetworkCycleData>> { - private static final String TAG = "CycleDataSummaryLoader"; - private final NetworkStatsManager mNetworkStatsManager; - private final String mSubId; - private final int mNetworkType; +public abstract class NetworkCycleDataLoader<D> extends AsyncTaskLoader<D> { + private static final String TAG = "NetworkCycleDataLoader"; + protected final NetworkStatsManager mNetworkStatsManager; + protected final String mSubId; + protected final int mNetworkType; private final NetworkPolicy mPolicy; private final NetworkTemplate mNetworkTemplate; @VisibleForTesting final INetworkStatsService mNetworkStatsService; - private NetworkCycleDataLoader(Builder builder) { + protected NetworkCycleDataLoader(Builder<?> builder) { super(builder.mContext); - mPolicy = builder.mPolicy; mSubId = builder.mSubId; mNetworkType = builder.mNetworkType; mNetworkTemplate = builder.mNetworkTemplate; @@ -67,6 +65,10 @@ public class NetworkCycleDataLoader extends AsyncTaskLoader<List<NetworkCycleDat builder.mContext.getSystemService(Context.NETWORK_STATS_SERVICE); mNetworkStatsService = INetworkStatsService.Stub.asInterface( ServiceManager.getService(Context.NETWORK_STATS_SERVICE)); + final NetworkPolicyEditor policyEditor = + new NetworkPolicyEditor(NetworkPolicyManager.from(builder.mContext)); + policyEditor.read(); + mPolicy = policyEditor.getPolicy(mNetworkTemplate); } @Override @@ -75,21 +77,25 @@ public class NetworkCycleDataLoader extends AsyncTaskLoader<List<NetworkCycleDat forceLoad(); } - @Override - public List<NetworkCycleData> loadInBackground() { + public D loadInBackground() { if (mPolicy == null) { - return loadFourWeeksData(); + loadFourWeeksData(); + } else { + loadPolicyData(); } - final List<NetworkCycleData> data = new ArrayList<>(); - final Iterator<Pair<ZonedDateTime, ZonedDateTime>> iterator = NetworkPolicyManager - .cycleIterator(mPolicy); + return getCycleUsage(); + } + + @VisibleForTesting + void loadPolicyData() { + final Iterator<Pair<ZonedDateTime, ZonedDateTime>> iterator = + NetworkPolicyManager.cycleIterator(mPolicy); while (iterator.hasNext()) { final Pair<ZonedDateTime, ZonedDateTime> cycle = iterator.next(); final long cycleStart = cycle.first.toInstant().toEpochMilli(); final long cycleEnd = cycle.second.toInstant().toEpochMilli(); - getUsage(cycleStart, cycleEnd, data); + recordUsage(cycleStart, cycleEnd); } - return data; } @Override @@ -105,8 +111,7 @@ public class NetworkCycleDataLoader extends AsyncTaskLoader<List<NetworkCycleDat } @VisibleForTesting - List<NetworkCycleData> loadFourWeeksData() { - final List<NetworkCycleData> data = new ArrayList<>(); + void loadFourWeeksData() { try { final INetworkStatsSession networkSession = mNetworkStatsService.openSession(); final NetworkStatsHistory networkHistory = networkSession.getHistoryForNetwork( @@ -116,8 +121,9 @@ public class NetworkCycleDataLoader extends AsyncTaskLoader<List<NetworkCycleDat long cycleEnd = historyEnd; while (cycleEnd > historyStart) { - final long cycleStart = cycleEnd - (DateUtils.WEEK_IN_MILLIS * 4); - getUsage(cycleStart, cycleEnd, data); + final long cycleStart = Math.max( + historyStart, cycleEnd - (DateUtils.WEEK_IN_MILLIS * 4)); + recordUsage(cycleStart, cycleEnd); cycleEnd = cycleStart; } @@ -125,29 +131,23 @@ public class NetworkCycleDataLoader extends AsyncTaskLoader<List<NetworkCycleDat } catch (RemoteException e) { throw new RuntimeException(e); } - return data; } @VisibleForTesting - void getUsage(long start, long end, @NonNull List<NetworkCycleData> data) { - try { - final NetworkStats stats = mNetworkStatsManager.querySummary( - mNetworkType, mSubId, start, end); - final long total = getTotalUsage(stats); - if (total > 0L) { - data.add(new NetworkCycleData.Builder() - .setStartTime(start) - .setEndTime(end) - .setTotalUsage(total) - .setUsageBuckets(getUsageBuckets(start, end)) - .build()); + abstract void recordUsage(long start, long end); + + abstract D getCycleUsage(); + + public static Builder<?> builder(Context context) { + return new Builder<NetworkCycleDataLoader>(context) { + @Override + public NetworkCycleDataLoader build() { + return null; } - } catch (RemoteException e) { - Log.e(TAG, "Exception querying network detail.", e); - } + }; } - private long getTotalUsage(NetworkStats stats) { + protected long getTotalUsage(NetworkStats stats) { long bytes = 0L; if (stats != null) { final NetworkStats.Bucket bucket = new NetworkStats.Bucket(); @@ -159,60 +159,47 @@ public class NetworkCycleDataLoader extends AsyncTaskLoader<List<NetworkCycleDat return bytes; } - private List<NetworkCycleData> getUsageBuckets(long start, long end) { - final List<NetworkCycleData> data = new ArrayList<>(); - long bucketStart = start; - long bucketEnd = start + NetworkCycleData.BUCKET_DURATION_MS; - while (bucketEnd <= end) { - long usage = 0L; - try { - final NetworkStats stats = mNetworkStatsManager.querySummary( - mNetworkType, mSubId, bucketStart, bucketEnd); - usage = getTotalUsage(stats); - } catch (RemoteException e) { - Log.e(TAG, "Exception querying network detail.", e); - } - data.add(new NetworkCycleData.Builder() - .setStartTime(bucketStart).setEndTime(bucketEnd).setTotalUsage(usage).build()); - bucketStart = bucketEnd; - bucketEnd += NetworkCycleData.BUCKET_DURATION_MS; - } - return data; - } - - public static class Builder { + public static abstract class Builder<T extends NetworkCycleDataLoader> { private final Context mContext; - private NetworkPolicy mPolicy; private String mSubId; private int mNetworkType; private NetworkTemplate mNetworkTemplate; - public Builder(Context context) { + public Builder (Context context) { mContext = context; } - public Builder setNetworkPolicy(NetworkPolicy policy) { - mPolicy = policy; - return this; - } - - public Builder setSubscriberId(String subId) { + public Builder<T> setSubscriberId(String subId) { mSubId = subId; return this; } - public Builder setNetworkType(int networkType) { - mNetworkType = networkType; - return this; - } - - public Builder setNetworkTemplate(NetworkTemplate template) { + public Builder<T> setNetworkTemplate(NetworkTemplate template) { mNetworkTemplate = template; + setNetworkType(); return this; } - public NetworkCycleDataLoader build() { - return new NetworkCycleDataLoader(this); + public abstract T build(); + + private void setNetworkType() { + if (mNetworkTemplate != null) { + final int matchRule = mNetworkTemplate.getMatchRule(); + switch (matchRule) { + case NetworkTemplate.MATCH_MOBILE: + case NetworkTemplate.MATCH_MOBILE_WILDCARD: + mNetworkType = ConnectivityManager.TYPE_MOBILE; + break; + case NetworkTemplate.MATCH_WIFI: + mNetworkType = ConnectivityManager.TYPE_WIFI; + break; + case NetworkTemplate.MATCH_ETHERNET: + mNetworkType = ConnectivityManager.TYPE_ETHERNET; + break; + default: + mNetworkType = ConnectivityManager.TYPE_MOBILE; + } + } } } diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java index fe0b35b4191c..089f773ec1e9 100644 --- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java +++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiStatusTracker.java @@ -163,6 +163,12 @@ public class WifiStatusTracker extends ConnectivityManager.NetworkCallback { ? null : AccessPoint.getSpeedLabel(mContext, scoredNetwork, rssi); } + /** Refresh the status label on Locale changed. */ + public void refreshLocale() { + updateStatusLabel(); + mCallback.run(); + } + private String getValidSsid(WifiInfo info) { String ssid = info.getSSID(); if (ssid != null && !WifiSsid.NONE.equals(ssid)) { diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/SapProfileTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/SapProfileTest.java new file mode 100644 index 000000000000..9bb53ee6a343 --- /dev/null +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/SapProfileTest.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.settingslib.bluetooth; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.when; +import static org.mockito.Mockito.verify; + +import android.bluetooth.BluetoothAdapter; +import android.bluetooth.BluetoothDevice; +import android.bluetooth.BluetoothSap; +import android.bluetooth.BluetoothProfile; + +import com.android.settingslib.SettingsLibRobolectricTestRunner; +import com.android.settingslib.testutils.shadow.ShadowBluetoothAdapter; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.robolectric.annotation.Config; +import org.robolectric.RuntimeEnvironment; +import org.robolectric.shadow.api.Shadow; + +@RunWith(SettingsLibRobolectricTestRunner.class) +@Config(shadows = {ShadowBluetoothAdapter.class}) +public class SapProfileTest { + + @Mock + private CachedBluetoothDeviceManager mDeviceManager; + @Mock + private LocalBluetoothProfileManager mProfileManager; + @Mock + private BluetoothSap mService; + @Mock + private CachedBluetoothDevice mCachedBluetoothDevice; + @Mock + private BluetoothDevice mBluetoothDevice; + private BluetoothProfile.ServiceListener mServiceListener; + private SapProfile mProfile; + private ShadowBluetoothAdapter mShadowBluetoothAdapter; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + mShadowBluetoothAdapter = Shadow.extract(BluetoothAdapter.getDefaultAdapter()); + mProfile = new SapProfile(RuntimeEnvironment.application, mDeviceManager, mProfileManager); + mServiceListener = mShadowBluetoothAdapter.getServiceListener(); + mServiceListener.onServiceConnected(BluetoothProfile.SAP, mService); + } + + @Test + public void connect_shouldConnectBluetoothSap() { + mProfile.connect(mBluetoothDevice); + verify(mService).connect(mBluetoothDevice); + } + + @Test + public void disconnect_shouldDisconnectBluetoothSap() { + mProfile.disconnect(mBluetoothDevice); + verify(mService).disconnect(mBluetoothDevice); + } + + @Test + public void getConnectionStatus_shouldReturnConnectionState() { + when(mService.getConnectionState(mBluetoothDevice)). + thenReturn(BluetoothProfile.STATE_CONNECTED); + assertThat(mProfile.getConnectionStatus(mBluetoothDevice)). + isEqualTo(BluetoothProfile.STATE_CONNECTED); + } +}
\ No newline at end of file diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/license/LicenseHtmlGeneratorFromXmlTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/license/LicenseHtmlGeneratorFromXmlTest.java index 96b2a1433f53..b00476b24921 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/license/LicenseHtmlGeneratorFromXmlTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/license/LicenseHtmlGeneratorFromXmlTest.java @@ -50,7 +50,7 @@ public class LicenseHtmlGeneratorFromXmlTest { + "<file-content contentId=\"0\"><![CDATA[license content #0]]></file-content>\n" + "</licenses2>"; - private static final String EXPECTED_HTML_STRING = + private static final String HTML_HEAD_STRING = "<html><head>\n" + "<style type=\"text/css\">\n" + "body { padding: 0; font-family: sans-serif; }\n" @@ -63,8 +63,12 @@ public class LicenseHtmlGeneratorFromXmlTest { + "</head>" + "<body topmargin=\"0\" leftmargin=\"0\" rightmargin=\"0\" bottommargin=\"0\">\n" + "<div class=\"toc\">\n" - + "<ul>\n" - + "<li><a href=\"#id0\">/file0</a></li>\n" + + "<ul>\n"; + + private static final String HTML_CUSTOM_HEADING = "Custom heading"; + + private static final String HTML_BODY_STRING = + "<li><a href=\"#id0\">/file0</a></li>\n" + "<li><a href=\"#id0\">/file1</a></li>\n" + "</ul>\n" + "</div><!-- table of contents -->\n" @@ -81,6 +85,11 @@ public class LicenseHtmlGeneratorFromXmlTest { + "</td></tr><!-- same-license -->\n" + "</table></body></html>\n"; + private static final String EXPECTED_HTML_STRING = HTML_HEAD_STRING + HTML_BODY_STRING; + + private static final String EXPECTED_HTML_STRING_WITH_CUSTOM_HEADING = + HTML_HEAD_STRING + HTML_CUSTOM_HEADING + "\n" + HTML_BODY_STRING; + @Test public void testParseValidXmlStream() throws XmlPullParserException, IOException { Map<String, String> fileNameToContentIdMap = new HashMap<String, String>(); @@ -117,7 +126,23 @@ public class LicenseHtmlGeneratorFromXmlTest { StringWriter output = new StringWriter(); LicenseHtmlGeneratorFromXml.generateHtml( - fileNameToContentIdMap, contentIdToFileContentMap, new PrintWriter(output)); + fileNameToContentIdMap, contentIdToFileContentMap, new PrintWriter(output), ""); assertThat(output.toString()).isEqualTo(EXPECTED_HTML_STRING); } + + @Test + public void testGenerateHtmlWithCustomHeading() { + Map<String, String> fileNameToContentIdMap = new HashMap<String, String>(); + Map<String, String> contentIdToFileContentMap = new HashMap<String, String>(); + + fileNameToContentIdMap.put("/file0", "0"); + fileNameToContentIdMap.put("/file1", "0"); + contentIdToFileContentMap.put("0", "license content #0"); + + StringWriter output = new StringWriter(); + LicenseHtmlGeneratorFromXml.generateHtml( + fileNameToContentIdMap, contentIdToFileContentMap, new PrintWriter(output), + HTML_CUSTOM_HEADING); + assertThat(output.toString()).isEqualTo(EXPECTED_HTML_STRING_WITH_CUSTOM_HEADING); + } } diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/license/LicenseHtmlLoaderCompatTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/license/LicenseHtmlLoaderCompatTest.java index 12a4e699fb76..c32cc99daf96 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/license/LicenseHtmlLoaderCompatTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/license/LicenseHtmlLoaderCompatTest.java @@ -142,7 +142,7 @@ public class LicenseHtmlLoaderCompatTest { } @Implementation - static boolean generateHtmlFile(List<File> xmlFiles, File htmlFile) { + static boolean generateHtmlFile(Context context, List<File> xmlFiles, File htmlFile) { return sGenerateHtmlFileSucceeded; } } diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/NetworkCycleChartDataLoaderTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/NetworkCycleChartDataLoaderTest.java new file mode 100644 index 000000000000..cad88b1f97a4 --- /dev/null +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/NetworkCycleChartDataLoaderTest.java @@ -0,0 +1,73 @@ +/* + * 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.settingslib.net; + +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.app.usage.NetworkStatsManager; +import android.content.Context; +import android.net.ConnectivityManager; +import android.net.NetworkPolicy; +import android.net.NetworkPolicyManager; +import android.os.RemoteException; +import android.text.format.DateUtils; + +import com.android.settingslib.SettingsLibRobolectricTestRunner; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +@RunWith(SettingsLibRobolectricTestRunner.class) +public class NetworkCycleChartDataLoaderTest { + + @Mock + private NetworkStatsManager mNetworkStatsManager; + @Mock + private NetworkPolicyManager mNetworkPolicyManager; + @Mock + private Context mContext; + + private NetworkCycleChartDataLoader mLoader; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + when(mContext.getSystemService(Context.NETWORK_STATS_SERVICE)) + .thenReturn(mNetworkStatsManager); + when(mContext.getSystemService(Context.NETWORK_POLICY_SERVICE)) + .thenReturn(mNetworkPolicyManager); + when(mNetworkPolicyManager.getNetworkPolicies()).thenReturn(new NetworkPolicy[0]); + } + + @Test + public void recordUsage_shouldQueryNetworkSummary() throws RemoteException { + final long end = System.currentTimeMillis(); + final long start = end - (DateUtils.WEEK_IN_MILLIS * 4); + final int networkType = ConnectivityManager.TYPE_MOBILE; + final String subId = "TestSubscriber"; + mLoader = NetworkCycleChartDataLoader.builder(mContext) + .setSubscriberId(subId).build(); + + mLoader.recordUsage(start, end); + + verify(mNetworkStatsManager).querySummary(networkType, subId, start, end); + } +} diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/NetworkCycleDataForUidLoaderTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/NetworkCycleDataForUidLoaderTest.java new file mode 100644 index 000000000000..2314f272c8ea --- /dev/null +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/NetworkCycleDataForUidLoaderTest.java @@ -0,0 +1,99 @@ +/* + * 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.settingslib.net; + +import static android.app.usage.NetworkStats.Bucket.STATE_FOREGROUND; +import static android.net.NetworkStats.TAG_NONE; + +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.app.usage.NetworkStatsManager; +import android.content.Context; +import android.net.ConnectivityManager; +import android.net.NetworkPolicy; +import android.net.NetworkPolicyManager; +import android.text.format.DateUtils; + +import com.android.settingslib.SettingsLibRobolectricTestRunner; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +@RunWith(SettingsLibRobolectricTestRunner.class) +public class NetworkCycleDataForUidLoaderTest { + + @Mock + private NetworkStatsManager mNetworkStatsManager; + @Mock + private NetworkPolicyManager mNetworkPolicyManager; + @Mock + private Context mContext; + + private NetworkCycleDataForUidLoader mLoader; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + when(mContext.getSystemService(Context.NETWORK_STATS_SERVICE)) + .thenReturn(mNetworkStatsManager); + when(mContext.getSystemService(Context.NETWORK_POLICY_SERVICE)) + .thenReturn(mNetworkPolicyManager); + when(mNetworkPolicyManager.getNetworkPolicies()).thenReturn(new NetworkPolicy[0]); + } + + @Test + public void recordUsage_shouldQueryNetworkDetailsForUidAndForegroundState() { + final long end = System.currentTimeMillis(); + final long start = end - (DateUtils.WEEK_IN_MILLIS * 4); + final int networkType = ConnectivityManager.TYPE_MOBILE; + final String subId = "TestSubscriber"; + final int uid = 1; + mLoader = spy(NetworkCycleDataForUidLoader.builder(mContext) + .setUid(uid).setSubscriberId(subId).build()); + doReturn(1024L).when(mLoader).getTotalUsage(any()); + + mLoader.recordUsage(start, end); + + verify(mNetworkStatsManager).queryDetailsForUid(networkType, subId, start, end, uid); + verify(mNetworkStatsManager).queryDetailsForUidTagState( + networkType, subId, start, end, uid, TAG_NONE, STATE_FOREGROUND); + } + + @Test + public void recordUsage_retrieveDetailIsFalse_shouldNotQueryNetworkForegroundState() { + final long end = System.currentTimeMillis(); + final long start = end - (DateUtils.WEEK_IN_MILLIS * 4); + final int networkType = ConnectivityManager.TYPE_MOBILE; + final String subId = "TestSubscriber"; + final int uid = 1; + mLoader = spy(NetworkCycleDataForUidLoader.builder(mContext) + .setRetrieveDetail(false).setUid(uid).setSubscriberId(subId).build()); + doReturn(1024L).when(mLoader).getTotalUsage(any()); + + mLoader.recordUsage(start, end); + verify(mNetworkStatsManager, never()).queryDetailsForUidTagState( + networkType, subId, start, end, uid, TAG_NONE, STATE_FOREGROUND); + } +} diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/NetworkCycleDataLoaderTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/NetworkCycleDataLoaderTest.java index 4c4207b23cab..9d60a97f8584 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/NetworkCycleDataLoaderTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/NetworkCycleDataLoaderTest.java @@ -16,13 +16,10 @@ package com.android.settingslib.net; -import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyInt; -import static org.mockito.Mockito.anyLong; -import static org.mockito.Mockito.doReturn; -import static org.mockito.Mockito.eq; -import static org.mockito.Mockito.mock; import static org.mockito.Matchers.nullable; +import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -33,6 +30,7 @@ import android.net.ConnectivityManager; import android.net.INetworkStatsService; import android.net.INetworkStatsSession; import android.net.NetworkPolicy; +import android.net.NetworkPolicyManager; import android.net.NetworkStatsHistory; import android.net.NetworkTemplate; import android.os.RemoteException; @@ -50,6 +48,7 @@ import org.robolectric.util.ReflectionHelpers; import java.time.ZonedDateTime; import java.util.Iterator; +import java.util.List; @RunWith(SettingsLibRobolectricTestRunner.class) public class NetworkCycleDataLoaderTest { @@ -57,6 +56,8 @@ public class NetworkCycleDataLoaderTest { @Mock private NetworkStatsManager mNetworkStatsManager; @Mock + private NetworkPolicyManager mNetworkPolicyManager; + @Mock private Context mContext; @Mock private NetworkPolicy mPolicy; @@ -65,20 +66,23 @@ public class NetworkCycleDataLoaderTest { @Mock private INetworkStatsService mNetworkStatsService; - private NetworkCycleDataLoader mLoader; + private NetworkCycleDataTestLoader mLoader; @Before public void setUp() { MockitoAnnotations.initMocks(this); when(mContext.getSystemService(Context.NETWORK_STATS_SERVICE)) .thenReturn(mNetworkStatsManager); + when(mContext.getSystemService(Context.NETWORK_POLICY_SERVICE)) + .thenReturn(mNetworkPolicyManager); when(mPolicy.cycleIterator()).thenReturn(mIterator); + when(mNetworkPolicyManager.getNetworkPolicies()).thenReturn(new NetworkPolicy[0]); } @Test public void loadInBackground_noNetworkPolicy_shouldLoad4WeeksData() { - mLoader = spy(new NetworkCycleDataLoader.Builder(mContext).build()); - doReturn(null).when(mLoader).loadFourWeeksData(); + mLoader = spy(new NetworkCycleDataTestLoader(mContext)); + doNothing().when(mLoader).loadFourWeeksData(); mLoader.loadInBackground(); @@ -86,31 +90,45 @@ public class NetworkCycleDataLoaderTest { } @Test - public void loadInBackground_shouldQueryNetworkSummary() throws RemoteException { + public void loadInBackground_hasNetworkPolicy_shouldLoadPolicyData() { + mLoader = spy(new NetworkCycleDataTestLoader(mContext)); + ReflectionHelpers.setField(mLoader, "mPolicy", mPolicy); + + mLoader.loadInBackground(); + + verify(mLoader).loadPolicyData(); + } + + @Test + public void loadPolicyData_shouldRecordUsageFromPolicyCycle() { final int networkType = ConnectivityManager.TYPE_MOBILE; final String subId = "TestSubscriber"; final ZonedDateTime now = ZonedDateTime.now(); final Range<ZonedDateTime> cycle = new Range<>(now, now); + final long nowInMs = now.toInstant().toEpochMilli(); // mock 1 cycle data. // hasNext() will be called internally in next(), hence setting it to return true twice. when(mIterator.hasNext()).thenReturn(true).thenReturn(true).thenReturn(false); when(mIterator.next()).thenReturn(cycle); - mLoader = new NetworkCycleDataLoader.Builder(mContext) - .setNetworkPolicy(mPolicy).setNetworkType(networkType).setSubscriberId(subId).build(); + mLoader = spy(new NetworkCycleDataTestLoader(mContext)); + ReflectionHelpers.setField(mLoader, "mPolicy", mPolicy); + ReflectionHelpers.setField(mLoader, "mNetworkType", networkType); + ReflectionHelpers.setField(mLoader, "mSubId", subId); - mLoader.loadInBackground(); + mLoader.loadPolicyData(); - verify(mNetworkStatsManager).querySummary(eq(networkType), eq(subId), anyLong(), anyLong()); + verify(mLoader).recordUsage(nowInMs, nowInMs); } @Test - public void loadFourWeeksData_shouldGetUsageForLast4Weeks() throws RemoteException { - mLoader = spy(new NetworkCycleDataLoader.Builder(mContext).build()); + public void loadFourWeeksData_shouldRecordUsageForLast4Weeks() throws RemoteException { + mLoader = spy(new NetworkCycleDataTestLoader(mContext)); ReflectionHelpers.setField(mLoader, "mNetworkStatsService", mNetworkStatsService); final INetworkStatsSession networkSession = mock(INetworkStatsSession.class); when(mNetworkStatsService.openSession()).thenReturn(networkSession); final NetworkStatsHistory networkHistory = mock(NetworkStatsHistory.class); - when(networkSession.getHistoryForNetwork(nullable(NetworkTemplate.class), anyInt())).thenReturn(networkHistory); + when(networkSession.getHistoryForNetwork(nullable(NetworkTemplate.class), anyInt())) + .thenReturn(networkHistory); final long now = System.currentTimeMillis(); final long fourWeeksAgo = now - (DateUtils.WEEK_IN_MILLIS * 4); when(networkHistory.getStart()).thenReturn(fourWeeksAgo); @@ -118,6 +136,23 @@ public class NetworkCycleDataLoaderTest { mLoader.loadFourWeeksData(); - verify(mLoader).getUsage(eq(fourWeeksAgo), eq(now), any()); + verify(mLoader).recordUsage(fourWeeksAgo, now); + } + + public class NetworkCycleDataTestLoader extends NetworkCycleDataLoader<List<NetworkCycleData>> { + + private NetworkCycleDataTestLoader(Context context) { + super(NetworkCycleDataLoader.builder(mContext)); + mContext = context; + } + + @Override + void recordUsage(long start, long end) { + } + + @Override + List<NetworkCycleData> getCycleUsage() { + return null; + } } } diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/testutils/FragmentTestUtils.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/testutils/FragmentTestUtils.java deleted file mode 100644 index d8e73b7db322..000000000000 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/testutils/FragmentTestUtils.java +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.settingslib.testutils; - -import android.os.Bundle; -import android.widget.LinearLayout; - -import androidx.fragment.app.Fragment; -import androidx.fragment.app.FragmentActivity; -import androidx.fragment.app.FragmentManager; - -import org.robolectric.Robolectric; - -/** - * Utilities for creating Fragments for testing. - * <p> - * TODO(b/111195449) - Duplicated from org.robolectric.shadows.support.v4.SupportFragmentTestUtil - */ -@Deprecated -public class FragmentTestUtils { - - public static void startFragment(Fragment fragment) { - buildFragmentManager(FragmentUtilActivity.class) - .beginTransaction().add(fragment, null).commit(); - } - - public static void startFragment(Fragment fragment, - Class<? extends FragmentActivity> activityClass) { - buildFragmentManager(activityClass) - .beginTransaction().add(fragment, null).commit(); - } - - public static void startVisibleFragment(Fragment fragment) { - buildFragmentManager(FragmentUtilActivity.class) - .beginTransaction().add(1, fragment, null).commit(); - } - - public static void startVisibleFragment(Fragment fragment, - Class<? extends FragmentActivity> activityClass, int containerViewId) { - buildFragmentManager(activityClass) - .beginTransaction().add(containerViewId, fragment, null).commit(); - } - - private static FragmentManager buildFragmentManager( - Class<? extends FragmentActivity> activityClass) { - FragmentActivity activity = Robolectric.setupActivity(activityClass); - return activity.getSupportFragmentManager(); - } - - private static class FragmentUtilActivity extends FragmentActivity { - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - LinearLayout view = new LinearLayout(this); - view.setId(1); - - setContentView(view); - } - } -} diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java index bd21b8353136..c09b76394340 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java @@ -659,6 +659,9 @@ class SettingsProtoDumpUtil { dumpSetting(s, p, Settings.Global.GPU_DEBUG_LAYERS, GlobalSettingsProto.Gpu.DEBUG_LAYERS); + dumpSetting(s, p, + Settings.Global.ANGLE_ENABLED_APP, + GlobalSettingsProto.Gpu.ANGLE_ENABLED_APP); p.end(gpuToken); final long hdmiToken = p.start(GlobalSettingsProto.HDMI); diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java index 3d193db392a4..18ec9c34e98c 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java @@ -870,7 +870,11 @@ public class SettingsProvider extends ContentProvider { } } if (newRestrictions.getBoolean(UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES) - != prevRestrictions.getBoolean(UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES)) { + != prevRestrictions.getBoolean(UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES) || + newRestrictions.getBoolean( + UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES_GLOBALLY) + != prevRestrictions.getBoolean( + UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES_GLOBALLY)) { final long identity = Binder.clearCallingIdentity(); try { synchronized (mLock) { diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml index da870bd134bf..5c654b44d1dc 100644 --- a/packages/Shell/AndroidManifest.xml +++ b/packages/Shell/AndroidManifest.xml @@ -151,6 +151,7 @@ <uses-permission android:name="android.permission.CONTROL_KEYGUARD" /> <uses-permission android:name="android.permission.SUSPEND_APPS" /> + <uses-permission android:name="android.permission.READ_CLIPBOARD_IN_BACKGROUND" /> <application android:label="@string/app_label" android:defaultToDeviceProtectedStorage="true" diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/phone/NavGesture.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/phone/NavGesture.java index aa2fb32f13a8..814324e63d19 100644 --- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/phone/NavGesture.java +++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/statusbar/phone/NavGesture.java @@ -20,6 +20,7 @@ import android.view.View; import com.android.systemui.plugins.Plugin; import com.android.systemui.plugins.annotations.ProvidesInterface; +import java.io.PrintWriter; @ProvidesInterface(action = NavGesture.ACTION, version = NavGesture.VERSION) public interface NavGesture extends Plugin { @@ -46,6 +47,8 @@ public interface NavGesture extends Plugin { public void onNavigationButtonLongPress(View v); public default void destroy() { } + + public default void dump(PrintWriter pw) { } } } diff --git a/packages/SystemUI/res/layout/qs_paged_tile_layout.xml b/packages/SystemUI/res/layout/qs_paged_tile_layout.xml index 11a01871b782..708326971454 100644 --- a/packages/SystemUI/res/layout/qs_paged_tile_layout.xml +++ b/packages/SystemUI/res/layout/qs_paged_tile_layout.xml @@ -20,10 +20,13 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_weight="1" - android:clipChildren="false" - android:clipToPadding="false" + android:clipChildren="true" + android:clipToPadding="true" + android:paddingStart="@dimen/notification_side_paddings" + android:paddingEnd="@dimen/notification_side_paddings" android:paddingBottom="@dimen/qs_paged_tile_layout_padding_bottom"> + <FrameLayout android:id="@+id/page_decor" android:layout_width="wrap_content" diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index 2b51aaab3805..e1c71fae7559 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -1081,7 +1081,7 @@ <string name="clear_all_notifications_text">Clear all</string> <!-- The text for the manage notifications link. [CHAR LIMIT=40] --> - <string name="manage_notifications_text">Manage notifications</string> + <string name="manage_notifications_text">Manage</string> <!-- The text to show in the notifications shade when dnd is suppressing notifications. [CHAR LIMIT=100] --> <string name="dnd_suppressing_shade_text">Notifications paused by Do Not Disturb</string> diff --git a/packages/SystemUI/shared/Android.bp b/packages/SystemUI/shared/Android.bp index 710b5f724add..defc49b5fac1 100644 --- a/packages/SystemUI/shared/Android.bp +++ b/packages/SystemUI/shared/Android.bp @@ -20,6 +20,10 @@ android_library { "src/**/I*.aidl", ], + static_libs: [ + "SystemUIPluginLib" + ], + // Enforce that the library is build agains java 7 so that there are // no compatibility issues with launcher java_version: "1.7", diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInitializer.java b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInitializer.java new file mode 100644 index 000000000000..985789415363 --- /dev/null +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInitializer.java @@ -0,0 +1,34 @@ +/* + * 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.shared.plugins; + +import android.content.Context; +import android.os.Looper; + +/** + * Provides necessary components for initializing {@link PluginManagerImpl}. + */ +public interface PluginInitializer { + + Looper getBgLooper(); + + /** + * This Runnable is run on the bg looper during initialization of {@link PluginManagerImpl}. + * It can be null. + */ + Runnable getBgInitCallback(); + + String[] getWhitelistedPlugins(Context context); +} diff --git a/packages/SystemUI/src/com/android/systemui/plugins/PluginInstanceManager.java b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInstanceManager.java index 7bc7e5f0095e..e80c079f60de 100644 --- a/packages/SystemUI/src/com/android/systemui/plugins/PluginInstanceManager.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginInstanceManager.java @@ -12,7 +12,7 @@ * permissions and limitations under the License. */ -package com.android.systemui.plugins; +package com.android.systemui.shared.plugins; import android.app.Notification; import android.app.Notification.Action; @@ -39,12 +39,14 @@ import android.view.LayoutInflater; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.messages.nano.SystemMessageProto.SystemMessage; -import com.android.systemui.plugins.VersionInfo.InvalidVersionException; +import com.android.systemui.plugins.Plugin; +import com.android.systemui.plugins.PluginFragment; +import com.android.systemui.plugins.PluginListener; +import com.android.systemui.shared.plugins.VersionInfo.InvalidVersionException; import java.util.ArrayList; import java.util.Arrays; import java.util.List; -import com.android.systemui.R; public class PluginInstanceManager<T extends Plugin> { @@ -71,8 +73,7 @@ public class PluginInstanceManager<T extends Plugin> { PluginInstanceManager(Context context, String action, PluginListener<T> listener, boolean allowMultiple, Looper looper, VersionInfo version, PluginManagerImpl manager) { this(context, context.getPackageManager(), action, listener, allowMultiple, looper, version, - manager, Build.IS_DEBUGGABLE, - context.getResources().getStringArray(R.array.config_pluginWhitelist)); + manager, Build.IS_DEBUGGABLE, manager.getWhitelistedPlugins()); } @VisibleForTesting @@ -114,7 +115,7 @@ public class PluginInstanceManager<T extends Plugin> { public void destroy() { if (DEBUG) Log.d(TAG, "stopListening"); - ArrayList<PluginInfo> plugins = new ArrayList<>(mPluginHandler.mPlugins); + ArrayList<PluginInfo> plugins = new ArrayList<PluginInfo>(mPluginHandler.mPlugins); for (PluginInfo plugin : plugins) { mMainHandler.obtainMessage(MainHandler.PLUGIN_DISCONNECTED, plugin.mPlugin).sendToTarget(); @@ -132,7 +133,7 @@ public class PluginInstanceManager<T extends Plugin> { public boolean checkAndDisable(String className) { boolean disableAny = false; - ArrayList<PluginInfo> plugins = new ArrayList<>(mPluginHandler.mPlugins); + ArrayList<PluginInfo> plugins = new ArrayList<PluginInfo>(mPluginHandler.mPlugins); for (PluginInfo info : plugins) { if (className.startsWith(info.mPackage)) { disable(info); @@ -143,7 +144,7 @@ public class PluginInstanceManager<T extends Plugin> { } public boolean disableAll() { - ArrayList<PluginInfo> plugins = new ArrayList<>(mPluginHandler.mPlugins); + ArrayList<PluginInfo> plugins = new ArrayList<PluginInfo>(mPluginHandler.mPlugins); for (int i = 0; i < plugins.size(); i++) { disable(plugins.get(i)); } @@ -165,7 +166,7 @@ public class PluginInstanceManager<T extends Plugin> { } public <T> boolean dependsOn(Plugin p, Class<T> cls) { - ArrayList<PluginInfo> plugins = new ArrayList<>(mPluginHandler.mPlugins); + ArrayList<PluginInfo> plugins = new ArrayList<PluginInfo>(mPluginHandler.mPlugins); for (PluginInfo info : plugins) { if (info.mPlugin.getClass().getName().equals(p.getClass().getName())) { return info.mVersion != null && info.mVersion.hasClass(cls); diff --git a/packages/SystemUI/src/com/android/systemui/plugins/PluginManager.java b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginManager.java index 298eaf18dce5..208f4fedfe27 100644 --- a/packages/SystemUI/src/com/android/systemui/plugins/PluginManager.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginManager.java @@ -12,10 +12,12 @@ * permissions and limitations under the License. */ -package com.android.systemui.plugins; +package com.android.systemui.shared.plugins; import android.text.TextUtils; +import com.android.systemui.plugins.Plugin; +import com.android.systemui.plugins.PluginListener; import com.android.systemui.plugins.annotations.ProvidesInterface; public interface PluginManager { @@ -40,14 +42,17 @@ public interface PluginManager { <T> boolean dependsOn(Plugin p, Class<T> cls); - static <P> String getAction(Class<P> cls) { - ProvidesInterface info = cls.getDeclaredAnnotation(ProvidesInterface.class); - if (info == null) { - throw new RuntimeException(cls + " doesn't provide an interface"); + class Helper { + public static <P> String getAction(Class<P> cls) { + ProvidesInterface info = cls.getDeclaredAnnotation(ProvidesInterface.class); + if (info == null) { + throw new RuntimeException(cls + " doesn't provide an interface"); + } + if (TextUtils.isEmpty(info.action())) { + throw new RuntimeException(cls + " doesn't provide an action"); + } + return info.action(); } - if (TextUtils.isEmpty(info.action())) { - throw new RuntimeException(cls + " doesn't provide an action"); - } - return info.action(); } + } diff --git a/packages/SystemUI/src/com/android/systemui/plugins/PluginManagerImpl.java b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginManagerImpl.java index 1cbf1fe0f2c4..7f1d161123f2 100644 --- a/packages/SystemUI/src/com/android/systemui/plugins/PluginManagerImpl.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginManagerImpl.java @@ -12,7 +12,7 @@ * permissions and limitations under the License. */ -package com.android.systemui.plugins; +package com.android.systemui.shared.plugins; import android.app.Notification; import android.app.Notification.Action; @@ -41,10 +41,11 @@ import android.widget.Toast; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.messages.nano.SystemMessageProto.SystemMessage; -import com.android.systemui.Dependency; -import com.android.systemui.R; -import com.android.systemui.plugins.PluginInstanceManager.PluginContextWrapper; -import com.android.systemui.plugins.PluginInstanceManager.PluginInfo; + +import com.android.systemui.plugins.Plugin; +import com.android.systemui.plugins.PluginListener; +import com.android.systemui.shared.plugins.PluginInstanceManager.PluginContextWrapper; +import com.android.systemui.shared.plugins.PluginInstanceManager.PluginInfo; import com.android.systemui.plugins.annotations.ProvidesInterface; import dalvik.system.PathClassLoader; @@ -79,31 +80,33 @@ public class PluginManagerImpl extends BroadcastReceiver implements PluginManage private Looper mLooper; private boolean mWtfsSet; - public PluginManagerImpl(Context context) { + public PluginManagerImpl(Context context, PluginInitializer initializer) { this(context, new PluginInstanceManagerFactory(), Build.IS_DEBUGGABLE, - context.getResources().getStringArray(R.array.config_pluginWhitelist), - Thread.getUncaughtExceptionPreHandler()); + Thread.getUncaughtExceptionPreHandler(), initializer); } @VisibleForTesting PluginManagerImpl(Context context, PluginInstanceManagerFactory factory, boolean debuggable, - String[] whitelistedPlugins, UncaughtExceptionHandler defaultHandler) { + UncaughtExceptionHandler defaultHandler, PluginInitializer initializer) { mContext = context; mFactory = factory; - mLooper = Dependency.get(Dependency.BG_LOOPER); + mLooper = initializer.getBgLooper(); isDebuggable = debuggable; - mWhitelistedPlugins.addAll(Arrays.asList(whitelistedPlugins)); + mWhitelistedPlugins.addAll(Arrays.asList(initializer.getWhitelistedPlugins(mContext))); mPluginPrefs = new PluginPrefs(mContext); PluginExceptionHandler uncaughtExceptionHandler = new PluginExceptionHandler( defaultHandler); Thread.setUncaughtExceptionPreHandler(uncaughtExceptionHandler); - new Handler(mLooper).post(() -> { - // Plugin dependencies that don't have another good home can go here, but - // dependencies that have better places to init can happen elsewhere. - Dependency.get(PluginDependencyProvider.class) - .allowPluginDependency(ActivityStarter.class); - }); + + Runnable bgRunnable = initializer.getBgInitCallback(); + if (bgRunnable != null) { + new Handler(mLooper).post(bgRunnable); + } + } + + public String[] getWhitelistedPlugins() { + return mWhitelistedPlugins.toArray(new String[0]); } public <T extends Plugin> T getOneShotPlugin(Class<T> cls) { @@ -121,7 +124,9 @@ public class PluginManagerImpl extends BroadcastReceiver implements PluginManage if (Looper.myLooper() != Looper.getMainLooper()) { throw new RuntimeException("Must be called from UI thread"); } - PluginInstanceManager<T> p = mFactory.createPluginInstanceManager(mContext, action, null, + // Passing null causes compiler to complain about incompatible (generic) types. + PluginListener<Plugin> dummy = null; + PluginInstanceManager<T> p = mFactory.createPluginInstanceManager(mContext, action, dummy, false, mLooper, cls, this); mPluginPrefs.addAction(action); PluginInfo<T> info = p.getPlugin(); @@ -140,7 +145,7 @@ public class PluginManagerImpl extends BroadcastReceiver implements PluginManage public <T extends Plugin> void addPluginListener(PluginListener<T> listener, Class<?> cls, boolean allowMultiple) { - addPluginListener(PluginManager.getAction(cls), listener, cls, allowMultiple); + addPluginListener(PluginManager.Helper.getAction(cls), listener, cls, allowMultiple); } public <T extends Plugin> void addPluginListener(String action, PluginListener<T> listener, @@ -293,8 +298,12 @@ public class PluginManagerImpl extends BroadcastReceiver implements PluginManage public void handleWtfs() { if (!mWtfsSet) { mWtfsSet = true; - Log.setWtfHandler((tag, what, system) -> { - throw new CrashWhilePluginActiveException(what); + Log.setWtfHandler(new Log.TerribleFailureHandler() { + @Override + public void onTerribleFailure(String tag, Log.TerribleFailure what, + boolean system) { + throw new CrashWhilePluginActiveException(what); + } }); } } diff --git a/packages/SystemUI/src/com/android/systemui/plugins/PluginPrefs.java b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginPrefs.java index 3671b3c1689f..c0c5d7051cea 100644 --- a/packages/SystemUI/src/com/android/systemui/plugins/PluginPrefs.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/PluginPrefs.java @@ -12,7 +12,7 @@ * permissions and limitations under the License. */ -package com.android.systemui.plugins; +package com.android.systemui.shared.plugins; import android.content.Context; import android.content.SharedPreferences; diff --git a/packages/SystemUI/src/com/android/systemui/plugins/VersionInfo.java b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/VersionInfo.java index facfd982408a..bb845cd87923 100644 --- a/packages/SystemUI/src/com/android/systemui/plugins/VersionInfo.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/plugins/VersionInfo.java @@ -12,7 +12,9 @@ * permissions and limitations under the License. */ -package com.android.systemui.plugins; +package com.android.systemui.shared.plugins; + +import android.util.ArrayMap; import com.android.systemui.plugins.annotations.Dependencies; import com.android.systemui.plugins.annotations.DependsOn; @@ -20,7 +22,7 @@ import com.android.systemui.plugins.annotations.ProvidesInterface; import com.android.systemui.plugins.annotations.Requirements; import com.android.systemui.plugins.annotations.Requires; -import android.util.ArrayMap; +import java.util.function.BiConsumer; public class VersionInfo { @@ -73,25 +75,32 @@ public class VersionInfo { } public void checkVersion(VersionInfo plugin) throws InvalidVersionException { - ArrayMap<Class<?>, Version> versions = new ArrayMap<>(mVersions); - plugin.mVersions.forEach((aClass, version) -> { - Version v = versions.remove(aClass); - if (v == null) { - v = createVersion(aClass); - } - if (v == null) { - throw new InvalidVersionException(aClass.getSimpleName() - + " does not provide an interface", false); - } - if (v.mVersion != version.mVersion) { - throw new InvalidVersionException(aClass, v.mVersion < version.mVersion, v.mVersion, - version.mVersion); + final ArrayMap<Class<?>, Version> versions = new ArrayMap<>(mVersions); + plugin.mVersions.forEach(new BiConsumer<Class<?>, Version>() { + @Override + public void accept(Class<?> aClass, Version version) { + Version v = versions.remove(aClass); + if (v == null) { + v = VersionInfo.this.createVersion(aClass); + } + if (v == null) { + throw new InvalidVersionException(aClass.getSimpleName() + + " does not provide an interface", false); + } + if (v.mVersion != version.mVersion) { + throw new InvalidVersionException(aClass, v.mVersion < version.mVersion, + v.mVersion, + version.mVersion); + } } }); - versions.forEach((aClass, version) -> { - if (version.mRequired) { - throw new InvalidVersionException("Missing required dependency " - + aClass.getSimpleName(), false); + versions.forEach(new BiConsumer<Class<?>, Version>() { + @Override + public void accept(Class<?> aClass, Version version) { + if (version.mRequired) { + throw new InvalidVersionException("Missing required dependency " + + aClass.getSimpleName(), false); + } } }); } diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/NavigationBarCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/NavigationBarCompat.java index d38cc0f608ce..69aea2c59029 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/NavigationBarCompat.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/NavigationBarCompat.java @@ -24,8 +24,6 @@ import android.util.DisplayMetrics; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; -import sun.misc.Resource; - public class NavigationBarCompat { /** * Touch slopes and thresholds for quick step operations. Drag slop is the point where the diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java index 5bbbc52f4cbc..28eff46db1d0 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java @@ -14,7 +14,7 @@ import androidx.annotation.VisibleForTesting; import com.android.systemui.Dependency; import com.android.systemui.plugins.ClockPlugin; import com.android.systemui.plugins.PluginListener; -import com.android.systemui.plugins.PluginManager; +import com.android.systemui.shared.plugins.PluginManager; /** * Switch to show plugin clock when plugin is connected, otherwise it will show default clock. diff --git a/packages/SystemUI/src/com/android/systemui/Dependency.java b/packages/SystemUI/src/com/android/systemui/Dependency.java index b2cf305d0533..fe1fe1aba908 100644 --- a/packages/SystemUI/src/com/android/systemui/Dependency.java +++ b/packages/SystemUI/src/com/android/systemui/Dependency.java @@ -39,9 +39,10 @@ import com.android.systemui.fragments.FragmentService; import com.android.systemui.keyguard.ScreenLifecycle; import com.android.systemui.keyguard.WakefulnessLifecycle; import com.android.systemui.plugins.ActivityStarter; +import com.android.systemui.plugins.PluginInitializerImpl; import com.android.systemui.plugins.PluginDependencyProvider; -import com.android.systemui.plugins.PluginManager; -import com.android.systemui.plugins.PluginManagerImpl; +import com.android.systemui.shared.plugins.PluginManager; +import com.android.systemui.shared.plugins.PluginManagerImpl; import com.android.systemui.plugins.VolumeDialogController; import com.android.systemui.power.EnhancedEstimates; import com.android.systemui.power.EnhancedEstimatesImpl; @@ -236,7 +237,7 @@ public class Dependency extends SystemUI { new DeviceProvisionedControllerImpl(mContext)); mProviders.put(PluginManager.class, () -> - new PluginManagerImpl(mContext)); + new PluginManagerImpl(mContext, new PluginInitializerImpl())); mProviders.put(AssistManager.class, () -> new AssistManager(getDependency(DeviceProvisionedController.class), mContext)); diff --git a/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java b/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java index 408e599bc289..77f4bf529f21 100644 --- a/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java +++ b/packages/SystemUI/src/com/android/systemui/ImageWallpaper.java @@ -256,6 +256,12 @@ public class ImageWallpaper extends WallpaperService { Log.d(TAG, "onSurfaceRedrawNeeded"); } super.onSurfaceRedrawNeeded(holder); + // At the end of this method we should have drawn into the surface. + // This means that the bitmap should be loaded synchronously if + // it was already unloaded. + if (mBackground == null) { + updateBitmap(mWallpaperManager.getBitmap(true /* hardware */)); + } mSurfaceRedrawNeeded = true; drawFrame(); } diff --git a/packages/SystemUI/src/com/android/systemui/PluginInflateContainer.java b/packages/SystemUI/src/com/android/systemui/PluginInflateContainer.java index ddd48330e679..f6ad62616a96 100644 --- a/packages/SystemUI/src/com/android/systemui/PluginInflateContainer.java +++ b/packages/SystemUI/src/com/android/systemui/PluginInflateContainer.java @@ -21,7 +21,7 @@ import android.util.Log; import android.view.View; import com.android.systemui.plugins.PluginListener; -import com.android.systemui.plugins.PluginManager; +import com.android.systemui.shared.plugins.PluginManager; import com.android.systemui.plugins.ViewProvider; /** diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java index b96a6049421b..78053b28c4c3 100644 --- a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java +++ b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java @@ -34,7 +34,7 @@ import android.util.TimingsTraceLog; import com.android.systemui.plugins.OverlayPlugin; import com.android.systemui.plugins.PluginListener; -import com.android.systemui.plugins.PluginManager; +import com.android.systemui.shared.plugins.PluginManager; import com.android.systemui.statusbar.phone.StatusBar; import com.android.systemui.statusbar.phone.StatusBarWindowController; import com.android.systemui.util.NotificationChannels; diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIService.java b/packages/SystemUI/src/com/android/systemui/SystemUIService.java index bb82a54c12f1..8e29841e887d 100644 --- a/packages/SystemUI/src/com/android/systemui/SystemUIService.java +++ b/packages/SystemUI/src/com/android/systemui/SystemUIService.java @@ -28,8 +28,8 @@ import java.io.FileDescriptor; import java.io.PrintWriter; import com.android.internal.os.BinderInternal; -import com.android.systemui.plugins.PluginManager; -import com.android.systemui.plugins.PluginManagerImpl; +import com.android.systemui.shared.plugins.PluginManager; +import com.android.systemui.shared.plugins.PluginManagerImpl; public class SystemUIService extends Service { diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java b/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java index e6026c14cd28..21b21d9f5527 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeLog.java @@ -44,7 +44,7 @@ public class DozeLog { public static final int PULSE_REASON_SENSOR_PICKUP = 3; public static final int PULSE_REASON_SENSOR_DOUBLE_TAP = 4; public static final int PULSE_REASON_SENSOR_LONG_PRESS = 5; - public static final int PULSE_REASON_SENSOR_REACH = 6; + public static final int PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN = 6; public static final int REASON_SENSOR_WAKE_UP = 7; private static boolean sRegisterKeyguardCallback = true; @@ -177,9 +177,9 @@ public class DozeLog { log("state " + state); } - public static void traceReachWakeUp() { + public static void traceWakeLockScreenWakeUp() { if (!ENABLED) return; - log("reachWakeUp"); + log("wakeLockScreenWakeUp"); } public static void traceProximityResult(Context context, boolean near, long millis, @@ -199,7 +199,7 @@ public class DozeLog { case PULSE_REASON_SENSOR_PICKUP: return "pickup"; case PULSE_REASON_SENSOR_DOUBLE_TAP: return "doubletap"; case PULSE_REASON_SENSOR_LONG_PRESS: return "longpress"; - case PULSE_REASON_SENSOR_REACH: return "reach"; + case PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN: return "wakeLockScreen"; case REASON_SENSOR_WAKE_UP: return "wakeup"; default: throw new IllegalArgumentException("bad reason: " + pulseReason); } diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java index f9dfb5d10403..701394763fbd 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeSensors.java @@ -113,10 +113,10 @@ public class DozeSensors { true /* reports touch coordinates */, true /* touchscreen */), new TriggerSensor( - findSensorWithType(config.reachSensorType()), - Settings.Secure.DOZE_REACH_GESTURE, + findSensorWithType(config.wakeLockScreenSensorType()), + Settings.Secure.DOZE_WAKE_LOCK_SCREEN_GESTURE, true /* configured */, - DozeLog.PULSE_REASON_SENSOR_REACH, + DozeLog.PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN, false /* reports touch coordinates */, false /* touchscreen */), new WakeScreenSensor(), diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeService.java b/packages/SystemUI/src/com/android/systemui/doze/DozeService.java index 73393047cc45..c61e10aa77ab 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeService.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeService.java @@ -26,7 +26,7 @@ import com.android.systemui.Dependency; import com.android.systemui.plugins.DozeServicePlugin; import com.android.systemui.plugins.DozeServicePlugin.RequestDoze; import com.android.systemui.plugins.PluginListener; -import com.android.systemui.plugins.PluginManager; +import com.android.systemui.shared.plugins.PluginManager; import java.io.FileDescriptor; import java.io.PrintWriter; diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java index 31548b93ea60..cb91d7815be5 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java @@ -128,7 +128,7 @@ public class DozeTriggers implements DozeMachine.Part { boolean isDoubleTap = pulseReason == DozeLog.PULSE_REASON_SENSOR_DOUBLE_TAP; boolean isPickup = pulseReason == DozeLog.PULSE_REASON_SENSOR_PICKUP; boolean isLongPress = pulseReason == DozeLog.PULSE_REASON_SENSOR_LONG_PRESS; - boolean isReach = pulseReason == DozeLog.PULSE_REASON_SENSOR_REACH; + boolean isWakeLockScreen = pulseReason == DozeLog.PULSE_REASON_SENSOR_WAKE_LOCK_SCREEN; if (isLongPress) { requestPulse(pulseReason, sensorPerformedProxCheck); @@ -141,7 +141,7 @@ public class DozeTriggers implements DozeMachine.Part { if (isDoubleTap) { mDozeHost.onDoubleTap(screenX, screenY); mMachine.wakeUp(); - } else if (isPickup || isReach) { + } else if (isPickup || isWakeLockScreen) { mMachine.wakeUp(); } else { mDozeHost.extendPulse(); @@ -156,8 +156,8 @@ public class DozeTriggers implements DozeMachine.Part { final boolean withinVibrationThreshold = timeSinceNotification < mDozeParameters.getPickupVibrationThreshold(); DozeLog.tracePickupWakeUp(mContext, withinVibrationThreshold); - } else if (isReach) { - DozeLog.traceReachWakeUp(); + } else if (isWakeLockScreen) { + DozeLog.traceWakeLockScreenWakeUp(); } } diff --git a/packages/SystemUI/src/com/android/systemui/plugins/PluginDependencyProvider.java b/packages/SystemUI/src/com/android/systemui/plugins/PluginDependencyProvider.java index c58d889270e2..03daa95567ee 100644 --- a/packages/SystemUI/src/com/android/systemui/plugins/PluginDependencyProvider.java +++ b/packages/SystemUI/src/com/android/systemui/plugins/PluginDependencyProvider.java @@ -18,6 +18,7 @@ import android.util.ArrayMap; import com.android.systemui.Dependency; import com.android.systemui.plugins.PluginDependency.DependencyProvider; +import com.android.systemui.shared.plugins.PluginManager; public class PluginDependencyProvider extends DependencyProvider { diff --git a/packages/SystemUI/src/com/android/systemui/plugins/PluginInitializerImpl.java b/packages/SystemUI/src/com/android/systemui/plugins/PluginInitializerImpl.java new file mode 100644 index 000000000000..108c2d05d76c --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/plugins/PluginInitializerImpl.java @@ -0,0 +1,47 @@ +/* + * 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.plugins; + +import android.content.Context; +import android.os.Looper; + +import com.android.systemui.Dependency; +import com.android.systemui.shared.plugins.PluginInitializer; +import com.android.systemui.R; + +public class PluginInitializerImpl implements PluginInitializer { + @Override + public Looper getBgLooper() { + return Dependency.get(Dependency.BG_LOOPER); + } + + @Override + public Runnable getBgInitCallback() { + return new Runnable() { + @Override + public void run() { + // Plugin dependencies that don't have another good home can go here, but + // dependencies that have better places to init can happen elsewhere. + Dependency.get(PluginDependencyProvider.class) + .allowPluginDependency(ActivityStarter.class); + } + }; + } + + @Override + public String[] getWhitelistedPlugins(Context context) { + return context.getResources().getStringArray(R.array.config_pluginWhitelist); + } +} diff --git a/packages/SystemUI/src/com/android/systemui/power/PowerUI.java b/packages/SystemUI/src/com/android/systemui/power/PowerUI.java index 0b9067e5dc1f..568a03954b36 100644 --- a/packages/SystemUI/src/com/android/systemui/power/PowerUI.java +++ b/packages/SystemUI/src/com/android/systemui/power/PowerUI.java @@ -301,9 +301,10 @@ public class PowerUI extends SystemUI { // 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]) { + if (mTimeRemaining <= mEnhancedEstimates.getSevereWarningThreshold() + || mBatteryLevel <= mLowBatteryReminderLevels[1]) { mSevereWarningShownThisChargeCycle = true; + mLowWarningShownThisChargeCycle = true; } else { mLowWarningShownThisChargeCycle = true; } diff --git a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java index fcd479ce6627..4b5ab2a640ff 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java +++ b/packages/SystemUI/src/com/android/systemui/qs/PagedTileLayout.java @@ -6,9 +6,7 @@ import android.animation.AnimatorSet; import android.animation.ObjectAnimator; import android.animation.PropertyValuesHolder; import android.content.Context; - -import androidx.viewpager.widget.PagerAdapter; -import androidx.viewpager.widget.ViewPager; +import android.content.res.Resources; import android.util.AttributeSet; import android.util.Log; import android.view.LayoutInflater; @@ -18,6 +16,9 @@ import android.view.animation.Interpolator; import android.view.animation.OvershootInterpolator; import android.widget.Scroller; +import androidx.viewpager.widget.PagerAdapter; +import androidx.viewpager.widget.ViewPager; + import com.android.systemui.R; import com.android.systemui.qs.QSPanel.QSTileLayout; import com.android.systemui.qs.QSPanel.TileRecord; @@ -224,7 +225,9 @@ public class PagedTileLayout extends ViewPager implements QSTileLayout { public boolean updateResources() { // Update bottom padding, useful for removing extra space once the panel page indicator is // hidden. - setPadding(0, 0, 0, + Resources res = getContext().getResources(); + final int sidePadding = res.getDimensionPixelSize(R.dimen.notification_side_paddings); + setPadding(sidePadding, 0, sidePadding, getContext().getResources().getDimensionPixelSize( R.dimen.qs_paged_tile_layout_padding_bottom)); boolean changed = false; diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java index 8b2e1d5eef64..e98ef4c09667 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java @@ -199,7 +199,11 @@ public class QSPanel extends LinearLayout implements Tunable, Callback, Brightne public void openDetails(String subPanel) { QSTile tile = getTile(subPanel); - showDetailAdapter(true, tile.getDetailAdapter(), new int[]{getWidth() / 2, 0}); + // If there's no tile with that name (as defined in QSFactoryImpl or other QSFactory), + // QSFactory will not be able to create a tile and getTile will return null + if (tile != null) { + showDetailAdapter(true, tile.getDetailAdapter(), new int[]{getWidth() / 2, 0}); + } } private QSTile getTile(String subPanel) { diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java b/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java index 86e69e34fb9e..cefeeb526968 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java @@ -31,7 +31,7 @@ import android.util.Log; import com.android.systemui.Dependency; import com.android.systemui.R; import com.android.systemui.plugins.PluginListener; -import com.android.systemui.plugins.PluginManager; +import com.android.systemui.shared.plugins.PluginManager; import com.android.systemui.plugins.qs.QSFactory; import com.android.systemui.plugins.qs.QSTileView; import com.android.systemui.plugins.qs.QSTile; diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java index 556786a16caf..6f847c86f8da 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanel.java @@ -253,7 +253,8 @@ public class QuickQSPanel extends QSPanel { final int availableWidth = getMeasuredWidth() - getPaddingStart() - getPaddingEnd(); final int leftoverWithespace = availableWidth - maxTiles * mCellWidth; - final int smallestHorizontalMarginNeeded = leftoverWithespace / (maxTiles - 1); + final int smallestHorizontalMarginNeeded; + smallestHorizontalMarginNeeded = leftoverWithespace / Math.max(1, maxTiles - 1); if (smallestHorizontalMarginNeeded > 0){ mCellMarginHorizontal = smallestHorizontalMarginNeeded; diff --git a/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java b/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java index 01ff72e6d152..e884302af075 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java +++ b/packages/SystemUI/src/com/android/systemui/qs/TileLayout.java @@ -103,8 +103,7 @@ public class TileLayout extends ViewGroup implements QSTileLayout { // it will show all its tiles. In this case, the tiles have to be entered before the // container is measured. Any change in the tiles, should trigger a remeasure. final int numTiles = mRecords.size(); - final int width = MeasureSpec.getSize(widthMeasureSpec) - - getPaddingStart() - getPaddingEnd(); + final int width = MeasureSpec.getSize(widthMeasureSpec); final int heightMode = MeasureSpec.getMode(heightMeasureSpec); if (heightMode == MeasureSpec.UNSPECIFIED) { mRows = (numTiles + mColumns - 1) / mColumns; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java index 9b1d334c614d..bce613a33859 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java @@ -72,7 +72,7 @@ import com.android.systemui.Interpolators; import com.android.systemui.R; import com.android.systemui.classifier.FalsingManager; import com.android.systemui.plugins.PluginListener; -import com.android.systemui.plugins.PluginManager; +import com.android.systemui.shared.plugins.PluginManager; import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin; import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin.MenuItem; import com.android.systemui.statusbar.notification.NotificationData; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarGestureHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarGestureHelper.java deleted file mode 100644 index 62d2099204e8..000000000000 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarGestureHelper.java +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright (C) 2014 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.systemui.statusbar.phone; - -import android.content.Context; -import android.graphics.Canvas; -import android.view.MotionEvent; -import android.view.View; -import com.android.systemui.SysUiServiceProvider; -import com.android.systemui.plugins.statusbar.phone.NavGesture.GestureHelper; - -/** - * TODO: Remove and replace with QuickStepController - */ -public class NavigationBarGestureHelper implements GestureHelper { - - private static final String TAG = "NavigationBarGestureHelper"; - - private NavigationBarView mNavigationBarView; - - private final QuickStepController mQuickStepController; - private final StatusBar mStatusBar; - - public NavigationBarGestureHelper(Context context) { - mStatusBar = SysUiServiceProvider.getComponent(context, StatusBar.class); - mQuickStepController = new QuickStepController(context); - } - - public void setComponents(NavigationBarView navigationBarView) { - mNavigationBarView = navigationBarView; - mQuickStepController.setComponents(mNavigationBarView); - } - - public void setBarState(boolean isVertical, boolean isRTL) { - mQuickStepController.setBarState(isVertical, isRTL); - } - - public boolean onInterceptTouchEvent(MotionEvent event) { - if (!canHandleGestures()) { - return false; - } - return mQuickStepController.onInterceptTouchEvent(event); - } - - public boolean onTouchEvent(MotionEvent event) { - if (!canHandleGestures()) { - return false; - } - return mQuickStepController.onTouchEvent(event); - } - - public void onDraw(Canvas canvas) { - mQuickStepController.onDraw(canvas); - } - - public void onLayout(boolean changed, int left, int top, int right, int bottom) { - mQuickStepController.onLayout(changed, left, top, right, bottom); - } - - public void onDarkIntensityChange(float intensity) { - mQuickStepController.onDarkIntensityChange(intensity); - } - - public void onNavigationButtonLongPress(View v) { - mQuickStepController.onNavigationButtonLongPress(v); - } - - private boolean canHandleGestures() { - return !mStatusBar.isKeyguardShowing(); - } -} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarInflaterView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarInflaterView.java index e6f2c33c33d2..52134d9d713c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarInflaterView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarInflaterView.java @@ -37,7 +37,7 @@ import com.android.systemui.Dependency; import com.android.systemui.OverviewProxyService; import com.android.systemui.R; import com.android.systemui.plugins.PluginListener; -import com.android.systemui.plugins.PluginManager; +import com.android.systemui.shared.plugins.PluginManager; import com.android.systemui.plugins.statusbar.phone.NavBarButtonProvider; import com.android.systemui.statusbar.phone.ReverseLinearLayout.ReverseRelativeLayout; import com.android.systemui.statusbar.policy.KeyButtonView; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java index e5c910069f82..16b2987558d3 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java @@ -40,7 +40,6 @@ import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.os.SystemProperties; -import androidx.annotation.ColorInt; import android.util.AttributeSet; import android.util.Log; import android.util.SparseArray; @@ -64,12 +63,11 @@ import com.android.systemui.R; import com.android.systemui.RecentsComponent; import com.android.systemui.SysUiServiceProvider; import com.android.systemui.plugins.PluginListener; -import com.android.systemui.plugins.PluginManager; +import com.android.systemui.shared.plugins.PluginManager; import com.android.systemui.plugins.statusbar.phone.NavGesture; import com.android.systemui.plugins.statusbar.phone.NavGesture.GestureHelper; import com.android.systemui.recents.Recents; import com.android.systemui.recents.RecentsOnboarding; -import com.android.systemui.shared.recents.IOverviewProxy; import com.android.systemui.shared.system.ActivityManagerWrapper; import com.android.systemui.shared.system.NavigationBarCompat; import com.android.systemui.shared.system.WindowManagerWrapper; @@ -314,8 +312,8 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav public void setComponents(NotificationPanelView panel) { mPanelView = panel; - if (mGestureHelper instanceof NavigationBarGestureHelper) { - ((NavigationBarGestureHelper) mGestureHelper).setComponents(this); + if (mGestureHelper instanceof QuickStepController) { + ((QuickStepController) mGestureHelper).setComponents(this); } } @@ -1071,7 +1069,7 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav @Override public void onPluginDisconnected(NavGesture plugin) { - NavigationBarGestureHelper defaultHelper = new NavigationBarGestureHelper(getContext()); + QuickStepController defaultHelper = new QuickStepController(getContext()); defaultHelper.setComponents(this); if (mGestureHelper != null) { mGestureHelper.destroy(); @@ -1117,6 +1115,7 @@ public class NavigationBarView extends FrameLayout implements PluginListener<Nav pw.println(" }"); mContextualButtonGroup.dump(pw); + mGestureHelper.dump(pw); mRecentsOnboarding.dump(pw); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationListenerWithPlugins.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationListenerWithPlugins.java index 9ff907b17564..9e561d13f347 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationListenerWithPlugins.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationListenerWithPlugins.java @@ -24,7 +24,7 @@ import com.android.systemui.Dependency; import com.android.systemui.plugins.NotificationListenerController; import com.android.systemui.plugins.NotificationListenerController.NotificationProvider; import com.android.systemui.plugins.PluginListener; -import com.android.systemui.plugins.PluginManager; +import com.android.systemui.shared.plugins.PluginManager; import java.util.ArrayList; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStepController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStepController.java index 30e6afa8465f..bce52a294e85 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStepController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStepController.java @@ -58,10 +58,12 @@ import com.android.systemui.Dependency; import com.android.systemui.Interpolators; import com.android.systemui.OverviewProxyService; import com.android.systemui.R; +import com.android.systemui.SysUiServiceProvider; import com.android.systemui.plugins.statusbar.phone.NavGesture.GestureHelper; import com.android.systemui.shared.recents.IOverviewProxy; import com.android.systemui.shared.recents.utilities.Utilities; import com.android.systemui.shared.system.NavigationBarCompat; +import java.io.PrintWriter; /** * Class to detect gestures on the navigation bar and implement quick scrub. @@ -117,6 +119,7 @@ public class QuickStepController implements GestureHelper { private final int mTrackEndPadding; private final int mHomeBackGestureDragLimit; private final Context mContext; + private final StatusBar mStatusBar; private final Matrix mTransformGlobalMatrix = new Matrix(); private final Matrix mTransformLocalMatrix = new Matrix(); private final Paint mTrackPaint = new Paint(); @@ -195,6 +198,7 @@ public class QuickStepController implements GestureHelper { public QuickStepController(Context context) { final Resources res = context.getResources(); mContext = context; + mStatusBar = SysUiServiceProvider.getComponent(context, StatusBar.class); mOverviewEventSender = Dependency.get(OverviewProxyService.class); mTrackThickness = res.getDimensionPixelSize(R.dimen.nav_quick_scrub_track_thickness); mTrackEndPadding = res.getDimensionPixelSize(R.dimen.nav_quick_scrub_track_edge_padding); @@ -218,6 +222,10 @@ public class QuickStepController implements GestureHelper { */ @Override public boolean onInterceptTouchEvent(MotionEvent event) { + if (mStatusBar.isKeyguardShowing()) { + // Disallow any handling when the keyguard is showing + return false; + } return handleTouchEvent(event); } @@ -227,6 +235,11 @@ public class QuickStepController implements GestureHelper { */ @Override public boolean onTouchEvent(MotionEvent event) { + if (mStatusBar.isKeyguardShowing()) { + // Disallow any handling when the keyguard is showing + return false; + } + // The same down event was just sent on intercept and therefore can be ignored here final boolean ignoreProxyDownEvent = event.getAction() == MotionEvent.ACTION_DOWN && mOverviewEventSender.getProxy() != null; @@ -483,6 +496,21 @@ public class QuickStepController implements GestureHelper { mHandler.removeCallbacksAndMessages(null); } + @Override + public void dump(PrintWriter pw) { + pw.println("QuickStepController {"); + pw.print(" "); pw.println("mQuickScrubActive=" + mQuickScrubActive); + pw.print(" "); pw.println("mQuickStepStarted=" + mQuickStepStarted); + pw.print(" "); pw.println("mAllowGestureDetection=" + mAllowGestureDetection); + pw.print(" "); pw.println("mBackGestureActive=" + mBackGestureActive); + pw.print(" "); pw.println("mCanPerformBack=" + mCanPerformBack); + pw.print(" "); pw.println("mNotificationsVisibleOnDown=" + mNotificationsVisibleOnDown); + pw.print(" "); pw.println("mIsVertical=" + mIsVertical); + pw.print(" "); pw.println("mIsRTL=" + mIsRTL); + pw.print(" "); pw.println("mIsInScreenPinning=" + mIsInScreenPinning); + pw.println("}"); + } + private void startQuickStep(MotionEvent event) { if (mIsInScreenPinning) { mNavigationBarView.showPinningEscapeToast(); 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 226b6453dbab..c3b87af612cc 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java @@ -2182,7 +2182,7 @@ public class StatusBar extends SystemUI implements DemoMode, } @Override - public void animateExpandSettingsPanel(String subPanel) { + public void animateExpandSettingsPanel(@Nullable String subPanel) { if (SPEW) Log.d(TAG, "animateExpand: mExpandedVisible=" + mExpandedVisible); if (!panelsEnabled()) { return; @@ -2191,7 +2191,6 @@ public class StatusBar extends SystemUI implements DemoMode, // Settings are not available in setup if (!mUserSetup) return; - if (subPanel != null) { mQSPanel.openDetails(subPanel); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ExtensionControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ExtensionControllerImpl.java index 6d75cfcb38f3..a6146a625193 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ExtensionControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ExtensionControllerImpl.java @@ -22,7 +22,7 @@ import android.util.ArrayMap; import com.android.systemui.Dependency; import com.android.systemui.plugins.Plugin; import com.android.systemui.plugins.PluginListener; -import com.android.systemui.plugins.PluginManager; +import com.android.systemui.shared.plugins.PluginManager; import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener; import com.android.systemui.tuner.TunerService; import com.android.systemui.tuner.TunerService.Tunable; @@ -71,7 +71,7 @@ public class ExtensionControllerImpl implements ExtensionController { @Override public <P extends T> ExtensionController.ExtensionBuilder<T> withPlugin(Class<P> cls) { - return withPlugin(cls, PluginManager.getAction(cls)); + return withPlugin(cls, PluginManager.Helper.getAction(cls)); } @Override 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 cf394043b091..24a28cb45952 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java @@ -668,6 +668,7 @@ public class NetworkControllerImpl extends BroadcastReceiver Locale current = mContext.getResources().getConfiguration().locale; if (!current.equals(mLocale)) { mLocale = current; + mWifiSignalController.refreshLocale(); notifyAllListeners(); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java index 0233ad1c86ed..693df884690a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/WifiSignalController.java @@ -73,6 +73,10 @@ public class WifiSignalController extends return new WifiState(); } + void refreshLocale() { + mWifiTracker.refreshLocale(); + } + @Override public void notifyListeners(SignalCallback callback) { // only show wifi in the cluster if connected or if wifi-only diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java index ef51bf06e411..8d2552f9b166 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java @@ -55,6 +55,7 @@ public class ZenModeControllerImpl extends CurrentUserTracker private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); private final ArrayList<Callback> mCallbacks = new ArrayList<>(); + private final Object mCallbacksLock = new Object(); private final Context mContext; private final GlobalSetting mModeSetting; private final GlobalSetting mConfigSetting; @@ -114,12 +115,16 @@ public class ZenModeControllerImpl extends CurrentUserTracker @Override public void addCallback(Callback callback) { - mCallbacks.add(callback); + synchronized (mCallbacksLock) { + mCallbacks.add(callback); + } } @Override public void removeCallback(Callback callback) { - mCallbacks.remove(callback); + synchronized (mCallbacksLock) { + mCallbacks.remove(callback); + } } @Override @@ -183,28 +188,40 @@ public class ZenModeControllerImpl extends CurrentUserTracker } private void fireNextAlarmChanged() { - Utils.safeForeach(mCallbacks, c -> c.onNextAlarmChanged()); + synchronized (mCallbacksLock) { + Utils.safeForeach(mCallbacks, c -> c.onNextAlarmChanged()); + } } private void fireEffectsSuppressorChanged() { - Utils.safeForeach(mCallbacks, c -> c.onEffectsSupressorChanged()); + synchronized (mCallbacksLock) { + Utils.safeForeach(mCallbacks, c -> c.onEffectsSupressorChanged()); + } } private void fireZenChanged(int zen) { - Utils.safeForeach(mCallbacks, c -> c.onZenChanged(zen)); + synchronized (mCallbacksLock) { + Utils.safeForeach(mCallbacks, c -> c.onZenChanged(zen)); + } } private void fireZenAvailableChanged(boolean available) { - Utils.safeForeach(mCallbacks, c -> c.onZenAvailableChanged(available)); + synchronized (mCallbacksLock) { + Utils.safeForeach(mCallbacks, c -> c.onZenAvailableChanged(available)); + } } private void fireManualRuleChanged(ZenRule rule) { - Utils.safeForeach(mCallbacks, c -> c.onManualRuleChanged(rule)); + synchronized (mCallbacksLock) { + Utils.safeForeach(mCallbacks, c -> c.onManualRuleChanged(rule)); + } } @VisibleForTesting protected void fireConfigChanged(ZenModeConfig config) { - Utils.safeForeach(mCallbacks, c -> c.onConfigChanged(config)); + synchronized (mCallbacksLock) { + Utils.safeForeach(mCallbacks, c -> c.onConfigChanged(config)); + } } @VisibleForTesting diff --git a/packages/SystemUI/src/com/android/systemui/tuner/PluginFragment.java b/packages/SystemUI/src/com/android/systemui/tuner/PluginFragment.java index c2948060d6bf..71414a266724 100644 --- a/packages/SystemUI/src/com/android/systemui/tuner/PluginFragment.java +++ b/packages/SystemUI/src/com/android/systemui/tuner/PluginFragment.java @@ -34,11 +34,10 @@ import android.util.ArraySet; import android.view.View; import com.android.systemui.R; -import com.android.systemui.plugins.PluginInstanceManager; -import com.android.systemui.plugins.PluginManager; -import com.android.systemui.plugins.PluginPrefs; +import com.android.systemui.shared.plugins.PluginInstanceManager; +import com.android.systemui.shared.plugins.PluginManager; +import com.android.systemui.shared.plugins.PluginPrefs; -import java.util.ArrayList; import java.util.List; import java.util.Set; diff --git a/packages/SystemUI/src/com/android/systemui/tuner/TunerFragment.java b/packages/SystemUI/src/com/android/systemui/tuner/TunerFragment.java index 088630fa3f56..5aa303530ae5 100644 --- a/packages/SystemUI/src/com/android/systemui/tuner/TunerFragment.java +++ b/packages/SystemUI/src/com/android/systemui/tuner/TunerFragment.java @@ -32,7 +32,7 @@ import com.android.internal.hardware.AmbientDisplayConfiguration; import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.systemui.R; -import com.android.systemui.plugins.PluginPrefs; +import com.android.systemui.shared.plugins.PluginPrefs; public class TunerFragment extends PreferenceFragment { diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java index 13c43f7009a1..4810b0b91c10 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java +++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java @@ -18,6 +18,7 @@ package com.android.systemui.volume; import static android.accessibilityservice.AccessibilityServiceInfo.FEEDBACK_ALL_MASK; import static android.accessibilityservice.AccessibilityServiceInfo.FEEDBACK_GENERIC; +import static android.app.ActivityManager.LOCK_TASK_MODE_NONE; import static android.media.AudioManager.RINGER_MODE_NORMAL; import static android.media.AudioManager.RINGER_MODE_SILENT; import static android.media.AudioManager.RINGER_MODE_VIBRATE; @@ -34,6 +35,7 @@ import static com.android.systemui.volume.Events.DISMISS_REASON_SETTINGS_CLICKED import android.accessibilityservice.AccessibilityServiceInfo; import android.animation.ObjectAnimator; import android.annotation.SuppressLint; +import android.app.ActivityManager; import android.app.Dialog; import android.app.KeyguardManager; import android.content.ContentResolver; @@ -129,6 +131,7 @@ public class VolumeDialogImpl implements VolumeDialog { private ConfigurableTexts mConfigurableTexts; private final SparseBooleanArray mDynamic = new SparseBooleanArray(); private final KeyguardManager mKeyguard; + private final ActivityManager mActivityManager; private final AccessibilityManagerWrapper mAccessibilityMgr; private final Object mSafetyWarningLock = new Object(); private final Accessibility mAccessibility = new Accessibility(); @@ -154,6 +157,7 @@ public class VolumeDialogImpl implements VolumeDialog { mContext = new ContextThemeWrapper(context, com.android.systemui.R.style.qs_theme); mController = Dependency.get(VolumeDialogController.class); mKeyguard = (KeyguardManager) mContext.getSystemService(Context.KEYGUARD_SERVICE); + mActivityManager = (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE); mAccessibilityMgr = Dependency.get(AccessibilityManagerWrapper.class); mDeviceProvisionedController = Dependency.get(DeviceProvisionedController.class); mShowActiveStreamOnly = showActiveStreamOnly(); @@ -431,7 +435,9 @@ public class VolumeDialogImpl implements VolumeDialog { public void initSettingsH() { if (mSettingsView != null) { mSettingsView.setVisibility( - mDeviceProvisionedController.isCurrentUserSetup() ? VISIBLE : GONE); + mDeviceProvisionedController.isCurrentUserSetup() && + mActivityManager.getLockTaskModeState() == LOCK_TASK_MODE_NONE ? + VISIBLE : GONE); } if (mSettingsIcon != null) { mSettingsIcon.setOnClickListener(v -> { diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java index e6e485740a7b..62ca3f34bd48 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchTest.java @@ -40,7 +40,7 @@ import android.widget.TextClock; import com.android.systemui.SysuiTestCase; import com.android.systemui.plugins.ClockPlugin; import com.android.systemui.plugins.PluginListener; -import com.android.systemui.plugins.PluginManager; +import com.android.systemui.shared.plugins.PluginManager; import org.junit.Before; import org.junit.Test; 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 a9d49f91e44e..b44630a0cec0 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/power/PowerUITest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/power/PowerUITest.java @@ -65,10 +65,12 @@ public class PowerUITest extends SysuiTestCase { private static final long ONE_HOUR_MILLIS = Duration.ofHours(1).toMillis(); public static final int BELOW_WARNING_BUCKET = -1; public static final long BELOW_HYBRID_THRESHOLD = TimeUnit.HOURS.toMillis(2); + public static final long BELOW_SEVERE_HYBRID_THRESHOLD = TimeUnit.MINUTES.toMillis(30); public static final long ABOVE_HYBRID_THRESHOLD = TimeUnit.HOURS.toMillis(4); private static final long ABOVE_CHARGE_CYCLE_THRESHOLD = Duration.ofHours(8).toMillis(); private static final int OLD_BATTERY_LEVEL_NINE = 9; private static final int OLD_BATTERY_LEVEL_10 = 10; + private static final long VERY_BELOW_SEVERE_HYBRID_THRESHOLD = TimeUnit.MINUTES.toMillis(15); private HardwarePropertiesManager mHardProps; private WarningsUI mMockWarnings; private PowerUI mPowerUI; @@ -467,6 +469,35 @@ public class PowerUITest extends SysuiTestCase { } @Test + public void testSevereWarning_countsAsLowAndSevere_WarningOnlyShownOnce() { + mPowerUI.start(); + when(mEnhancedEstimates.isHybridNotificationEnabled()).thenReturn(true); + when(mEnhancedEstimates.getLowWarningThreshold()).thenReturn(PowerUI.THREE_HOURS_IN_MILLIS); + when(mEnhancedEstimates.getSevereWarningThreshold()).thenReturn(ONE_HOUR_MILLIS); + when(mEnhancedEstimates.getEstimate()) + .thenReturn(new Estimate(BELOW_SEVERE_HYBRID_THRESHOLD, true)); + mPowerUI.mBatteryStatus = BatteryManager.BATTERY_HEALTH_GOOD; + + // reduce battery level to handle time based trigger -> level trigger interactions + mPowerUI.mBatteryLevel = 5; + boolean shouldShow = + mPowerUI.shouldShowLowBatteryWarning(UNPLUGGED, UNPLUGGED, ABOVE_WARNING_BUCKET, + ABOVE_WARNING_BUCKET, BELOW_SEVERE_HYBRID_THRESHOLD, + POWER_SAVER_OFF, BatteryManager.BATTERY_HEALTH_GOOD); + assertTrue(shouldShow); + + // actually run the end to end since it handles changing the internal state. + mPowerUI.maybeShowBatteryWarning(OLD_BATTERY_LEVEL_10, UNPLUGGED, UNPLUGGED, + ABOVE_WARNING_BUCKET, ABOVE_WARNING_BUCKET); + + shouldShow = + mPowerUI.shouldShowLowBatteryWarning(UNPLUGGED, UNPLUGGED, ABOVE_WARNING_BUCKET, + ABOVE_WARNING_BUCKET, VERY_BELOW_SEVERE_HYBRID_THRESHOLD, + POWER_SAVER_OFF, BatteryManager.BATTERY_HEALTH_GOOD); + assertFalse(shouldShow); + } + + @Test public void testMaybeShowBatteryWarning_onlyQueriesEstimateOnBatteryLevelChangeOrNull() { mPowerUI.start(); Estimate estimate = new Estimate(BELOW_HYBRID_THRESHOLD, true); diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.java index 85cdfcc2ce09..12a122a5c734 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.java @@ -14,8 +14,12 @@ package com.android.systemui.qs; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -23,15 +27,21 @@ import android.support.test.filters.SmallTest; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import android.testing.TestableLooper.RunWithLooper; +import android.view.ViewGroup; +import android.widget.FrameLayout; import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.systemui.SysuiTestCase; +import com.android.systemui.plugins.qs.QSTile; +import com.android.systemui.plugins.qs.QSTileView; import com.android.systemui.qs.customize.QSCustomizer; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; import java.util.Collections; @@ -41,19 +51,37 @@ import java.util.Collections; public class QSPanelTest extends SysuiTestCase { private MetricsLogger mMetricsLogger; + private TestableLooper mTestableLooper; private QSPanel mQsPanel; + @Mock private QSTileHost mHost; + @Mock private QSCustomizer mCustomizer; + @Mock + private QSTile dndTile; + private ViewGroup mParentView; + @Mock + private QSDetail.Callback mCallback; @Before public void setup() throws Exception { - TestableLooper.get(this).runWithLooper(() -> { + MockitoAnnotations.initMocks(this); + + mTestableLooper = TestableLooper.get(this); + mTestableLooper.runWithLooper(() -> { mMetricsLogger = mDependency.injectMockDependency(MetricsLogger.class); mQsPanel = new QSPanel(mContext, null); - mHost = mock(QSTileHost.class); + // Provides a parent with non-zero size for QSPanel + mParentView = new FrameLayout(mContext); + mParentView.addView(mQsPanel); + + when(dndTile.getTileSpec()).thenReturn("dnd"); when(mHost.getTiles()).thenReturn(Collections.emptyList()); - mCustomizer = mock(QSCustomizer.class); + when(mHost.createTileView(any(), anyBoolean())).thenReturn(mock(QSTileView.class)); + mQsPanel.setHost(mHost, mCustomizer); + mQsPanel.addTile(dndTile, true); + mQsPanel.setCallback(mCallback); }); } @@ -64,4 +92,31 @@ public class QSPanelTest extends SysuiTestCase { mQsPanel.setExpanded(false); verify(mMetricsLogger).visibility(eq(MetricsEvent.QS_PANEL), eq(false)); } + + @Test + public void testOpenDetailsWithExistingTile_NoException() { + mTestableLooper.processAllMessages(); + mQsPanel.openDetails("dnd"); + mTestableLooper.processAllMessages(); + + verify(mCallback).onShowingDetail(any(), anyInt(), anyInt()); + } + +/* @Test + public void testOpenDetailsWithNullParameter_NoException() { + mTestableLooper.processAllMessages(); + mQsPanel.openDetails(null); + mTestableLooper.processAllMessages(); + + verify(mCallback, never()).onShowingDetail(any(), anyInt(), anyInt()); + }*/ + + @Test + public void testOpenDetailsWithNonExistingTile_NoException() { + mTestableLooper.processAllMessages(); + mQsPanel.openDetails("invalid-name"); + mTestableLooper.processAllMessages(); + + verify(mCallback, never()).onShowingDetail(any(), anyInt(), anyInt()); + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/plugins/PluginInstanceManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginInstanceManagerTest.java index 19974f8fc710..6d1ff8c06acf 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/plugins/PluginInstanceManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginInstanceManagerTest.java @@ -12,7 +12,7 @@ * permissions and limitations under the License. */ -package com.android.systemui.plugins; +package com.android.systemui.shared.plugins; import static junit.framework.Assert.assertFalse; import static junit.framework.Assert.assertNotNull; @@ -27,8 +27,10 @@ import static org.mockito.Mockito.when; import com.android.internal.messages.nano.SystemMessageProto.SystemMessage; import com.android.systemui.SysuiTestCase; -import com.android.systemui.plugins.PluginInstanceManager.PluginInfo; -import com.android.systemui.plugins.VersionInfo.InvalidVersionException; +import com.android.systemui.plugins.Plugin; +import com.android.systemui.plugins.PluginListener; +import com.android.systemui.shared.plugins.PluginInstanceManager.PluginInfo; +import com.android.systemui.shared.plugins.VersionInfo.InvalidVersionException; import com.android.systemui.plugins.annotations.Requires; import org.junit.After; diff --git a/packages/SystemUI/tests/src/com/android/systemui/plugins/PluginManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginManagerTest.java index 438f9e49699b..3c7020569db4 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/plugins/PluginManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/PluginManagerTest.java @@ -11,7 +11,7 @@ * KIND, either express or implied. See the License for the specific language governing * permissions and limitations under the License. */ -package com.android.systemui.plugins; +package com.android.systemui.shared.plugins; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; @@ -27,6 +27,7 @@ import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; import android.net.Uri; +import android.support.test.runner.AndroidJUnit4; import android.test.suitebuilder.annotation.SmallTest; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; @@ -35,9 +36,12 @@ import android.testing.TestableLooper.RunWithLooper; import com.android.internal.messages.nano.SystemMessageProto.SystemMessage; import com.android.systemui.Dependency; import com.android.systemui.SysuiTestCase; -import com.android.systemui.plugins.PluginInstanceManager.PluginInfo; -import com.android.systemui.plugins.PluginManagerImpl.PluginInstanceManagerFactory; +import com.android.systemui.plugins.Plugin; +import com.android.systemui.plugins.PluginInitializerImpl; +import com.android.systemui.plugins.PluginListener; import com.android.systemui.plugins.annotations.ProvidesInterface; +import com.android.systemui.shared.plugins.PluginInstanceManager.PluginInfo; +import com.android.systemui.shared.plugins.PluginManagerImpl.PluginInstanceManagerFactory; import org.junit.Before; import org.junit.Test; @@ -74,8 +78,14 @@ public class PluginManagerTest extends SysuiTestCase { when(mMockFactory.createPluginInstanceManager(Mockito.any(), Mockito.any(), Mockito.any(), Mockito.anyBoolean(), Mockito.any(), Mockito.any(), Mockito.any())) .thenReturn(mMockPluginInstance); - mPluginManager = new PluginManagerImpl(getContext(), mMockFactory, true, new String[0], - mMockExceptionHandler); + + mPluginManager = new PluginManagerImpl(getContext(), mMockFactory, true, + mMockExceptionHandler, new PluginInitializerImpl() { + @Override + public String[] getWhitelistedPlugins(Context context) { + return new String[0]; + } + }); resetExceptionHandler(); mMockListener = mock(PluginListener.class); } @@ -109,7 +119,12 @@ public class PluginManagerTest extends SysuiTestCase { @RunWithLooper(setAsMainLooper = true) public void testNonDebuggable_noWhitelist() { mPluginManager = new PluginManagerImpl(getContext(), mMockFactory, false, - new String[0], mMockExceptionHandler); + mMockExceptionHandler, new PluginInitializerImpl() { + @Override + public String[] getWhitelistedPlugins(Context context) { + return new String[0]; + } + }); resetExceptionHandler(); mPluginManager.addPluginListener("myAction", mMockListener, TestPlugin.class); @@ -121,7 +136,12 @@ public class PluginManagerTest extends SysuiTestCase { @RunWithLooper(setAsMainLooper = true) public void testNonDebuggable_whitelistedPkg() { mPluginManager = new PluginManagerImpl(getContext(), mMockFactory, false, - new String[] {WHITELISTED_PACKAGE}, mMockExceptionHandler); + mMockExceptionHandler, new PluginInitializerImpl() { + @Override + public String[] getWhitelistedPlugins(Context context) { + return new String[] {WHITELISTED_PACKAGE}; + } + }); resetExceptionHandler(); mPluginManager.addPluginListener("myAction", mMockListener, TestPlugin.class); diff --git a/packages/SystemUI/tests/src/com/android/systemui/plugins/VersionInfoTest.java b/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/VersionInfoTest.java index 0b4d9b525c1b..9bad78d2d45c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/plugins/VersionInfoTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/shared/plugins/VersionInfoTest.java @@ -12,7 +12,7 @@ * permissions and limitations under the License. */ -package com.android.systemui.plugins; +package com.android.systemui.shared.plugins; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; @@ -20,7 +20,8 @@ import static org.junit.Assert.assertTrue; import android.support.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; -import com.android.systemui.plugins.VersionInfo.InvalidVersionException; +import com.android.systemui.plugins.OverlayPlugin; +import com.android.systemui.shared.plugins.VersionInfo.InvalidVersionException; import com.android.systemui.plugins.annotations.Requires; import com.android.systemui.plugins.qs.QS; import com.android.systemui.plugins.qs.DetailAdapter; diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/ExtensionControllerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/ExtensionControllerImplTest.java index b22a6468f5fa..1cceefa7910c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/ExtensionControllerImplTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/ExtensionControllerImplTest.java @@ -33,7 +33,7 @@ import com.android.systemui.SysuiTestCase; import com.android.systemui.plugins.OverlayPlugin; import com.android.systemui.plugins.Plugin; import com.android.systemui.plugins.PluginListener; -import com.android.systemui.plugins.PluginManager; +import com.android.systemui.shared.plugins.PluginManager; import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener; import com.android.systemui.statusbar.policy.ExtensionController.Extension; import com.android.systemui.statusbar.policy.ExtensionController.TunerFactory; diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakePluginManager.java b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakePluginManager.java index 0a83a896dfaf..5f54bceb6b9b 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakePluginManager.java +++ b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakePluginManager.java @@ -14,12 +14,11 @@ package com.android.systemui.utils.leaks; -import android.content.Context; import android.testing.LeakCheck; import com.android.systemui.plugins.Plugin; import com.android.systemui.plugins.PluginListener; -import com.android.systemui.plugins.PluginManager; +import com.android.systemui.shared.plugins.PluginManager; public class FakePluginManager implements PluginManager { diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/LeakCheckedTest.java b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/LeakCheckedTest.java index ecda9620f7fe..f47912623e1f 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/LeakCheckedTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/LeakCheckedTest.java @@ -20,7 +20,7 @@ import android.testing.LeakCheck; import android.util.ArrayMap; import com.android.systemui.SysuiTestCase; -import com.android.systemui.plugins.PluginManager; +import com.android.systemui.shared.plugins.PluginManager; import com.android.systemui.statusbar.phone.ManagedProfileController; import com.android.systemui.statusbar.phone.StatusBarIconController; import com.android.systemui.statusbar.policy.BatteryController; diff --git a/proto/src/metrics_constants/metrics_constants.proto b/proto/src/metrics_constants/metrics_constants.proto index 90c10fdcbfde..5e87707c39e6 100644 --- a/proto/src/metrics_constants/metrics_constants.proto +++ b/proto/src/metrics_constants/metrics_constants.proto @@ -5926,7 +5926,7 @@ message MetricsEvent { // Tag used to determine what type of charging was started/ended // 1 = Plugged AC // 2 = Plugged USB - // 3 = Wireless + // 4 = Wireless FIELD_PLUG_TYPE = 1421; // ACTION: USB-C Connector connected. @@ -6447,7 +6447,7 @@ message MetricsEvent { // OPEN: Settings > System > Input & Gesture > Reach up gesture // OS: Q - SETTINGS_GESTURE_REACH = 1557; + SETTINGS_GESTURE_WAKE_LOCK_SCREEN = 1557; // OPEN: Emergency dialer opened // CLOSE: Emergency dialer closed diff --git a/proto/src/wifi.proto b/proto/src/wifi.proto index 7f8989d8548e..033e99605d3f 100644 --- a/proto/src/wifi.proto +++ b/proto/src/wifi.proto @@ -479,6 +479,9 @@ message WifiLog { // Hardware revision (EVT, DVT, PVT etc.) optional string hardware_revision = 124; + + // Total wifi link layer usage data over the logging duration in ms. + optional WifiLinkLayerUsageStats wifi_link_layer_usage_stats = 125; } // Information that gets logged for every WiFi connection. @@ -1654,4 +1657,21 @@ message PasspointProfileTypeCount { // Num of installed Passpoint profile with same eap method optional int32 count = 2; +} + +message WifiLinkLayerUsageStats { + // Total logging duration in ms. + optional int64 logging_duration_ms = 1; + + // Total time the wifi radio is on in ms over the logging duration. + optional int64 radio_on_time_ms = 2; + + // Total time the wifi radio is doing tx in ms over the logging duration. + optional int64 radio_tx_time_ms = 3; + + // Total time the wifi radio is doing rx in ms over the logging duration. + optional int64 radio_rx_time_ms = 4; + + // Total time the wifi radio is scanning in ms over the logging duration. + optional int64 radio_scan_time_ms = 5; }
\ No newline at end of file diff --git a/services/core/java/com/android/server/AlarmManagerService.java b/services/core/java/com/android/server/AlarmManagerService.java index ad2f82c9b19f..af33cbc0ac1c 100644 --- a/services/core/java/com/android/server/AlarmManagerService.java +++ b/services/core/java/com/android/server/AlarmManagerService.java @@ -142,6 +142,8 @@ class AlarmManagerService extends SystemService { static final boolean RECORD_DEVICE_IDLE_ALARMS = false; static final String TIMEZONE_PROPERTY = "persist.sys.timezone"; + static final int TICK_HISTORY_DEPTH = 10; + // Indices into the APP_STANDBY_MIN_DELAYS and KEYS_APP_STANDBY_DELAY arrays static final int ACTIVE_INDEX = 0; static final int WORKING_INDEX = 1; @@ -176,21 +178,25 @@ class AlarmManagerService extends SystemService { private long mNextNonWakeUpSetAt; private long mLastWakeup; private long mLastTrigger; + private long mLastTickSet; - private long mLastTickIssued; // elapsed private long mLastTickReceived; private long mLastTickAdded; private long mLastTickRemoved; + // ring buffer of recent TIME_TICK issuance, in the elapsed timebase + private final long[] mTickHistory = new long[TICK_HISTORY_DEPTH]; + private int mNextTickHistory; + private final Injector mInjector; int mBroadcastRefCount = 0; PowerManager.WakeLock mWakeLock; - boolean mLastWakeLockUnimportantForLogging; ArrayList<Alarm> mPendingNonWakeupAlarms = new ArrayList<>(); ArrayList<InFlight> mInFlight = new ArrayList<>(); AlarmHandler mHandler; ClockReceiver mClockReceiver; final DeliveryTracker mDeliveryTracker = new DeliveryTracker(); - PendingIntent mTimeTickSender; + Intent mTimeTickIntent; + IAlarmListener mTimeTickTrigger; PendingIntent mDateChangeSender; Random mRandom; boolean mInteractive = true; @@ -509,7 +515,7 @@ class AlarmManagerService extends SystemService { end = clampPositive(seed.maxWhenElapsed); flags = seed.flags; alarms.add(seed); - if (seed.operation == mTimeTickSender) { + if (seed.listener == mTimeTickTrigger) { mLastTickAdded = mInjector.getCurrentTimeMillis(); } } @@ -534,7 +540,7 @@ class AlarmManagerService extends SystemService { index = 0 - index - 1; } alarms.add(index, alarm); - if (alarm.operation == mTimeTickSender) { + if (alarm.listener == mTimeTickTrigger) { mLastTickAdded = mInjector.getCurrentTimeMillis(); } if (DEBUG_BATCH) { @@ -572,7 +578,7 @@ class AlarmManagerService extends SystemService { if (alarm.alarmClock != null) { mNextAlarmClockMayChange = true; } - if (alarm.operation == mTimeTickSender) { + if (alarm.listener == mTimeTickTrigger) { mLastTickRemoved = mInjector.getCurrentTimeMillis(); } } else { @@ -690,8 +696,7 @@ class AlarmManagerService extends SystemService { Alarm a = alarms.get(i); final int alarmPrio; - if (a.operation != null - && Intent.ACTION_TIME_TICK.equals(a.operation.getIntent().getAction())) { + if (a.listener == mTimeTickTrigger) { alarmPrio = PRIO_TICK; } else if (a.wakeup) { alarmPrio = PRIO_WAKEUP; @@ -823,7 +828,7 @@ class AlarmManagerService extends SystemService { } final int batchSize = alarms.size(); for (int j = 0; j < batchSize; j++) { - if (alarms.get(j).operation == mTimeTickSender) { + if (alarms.get(j).listener == mTimeTickTrigger) { return true; } } @@ -1111,10 +1116,7 @@ class AlarmManagerService extends SystemService { updateNextAlarmClockLocked(); // And send a TIME_TICK right now, since it is important to get the UI updated. - try { - mTimeTickSender.send(); - } catch (PendingIntent.CanceledException e) { - } + mHandler.post(() -> getContext().sendBroadcastAsUser(mTimeTickIntent, UserHandle.ALL)); } static final class InFlight { @@ -1312,12 +1314,36 @@ class AlarmManagerService extends SystemService { } mWakeLock = mInjector.getAlarmWakeLock(); - mTimeTickSender = PendingIntent.getBroadcastAsUser(getContext(), 0, - new Intent(Intent.ACTION_TIME_TICK).addFlags( - Intent.FLAG_RECEIVER_REGISTERED_ONLY - | Intent.FLAG_RECEIVER_FOREGROUND - | Intent.FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS), 0, - UserHandle.ALL); + mTimeTickIntent = new Intent(Intent.ACTION_TIME_TICK).addFlags( + Intent.FLAG_RECEIVER_REGISTERED_ONLY + | Intent.FLAG_RECEIVER_FOREGROUND + | Intent.FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS); + + mTimeTickTrigger = new IAlarmListener.Stub() { + @Override + public void doAlarm(final IAlarmCompleteListener callback) throws RemoteException { + if (DEBUG_BATCH) { + Slog.v(TAG, "Received TIME_TICK alarm; rescheduling"); + } + + // Via handler because dispatch invokes this within its lock. OnAlarmListener + // takes care of this automatically, but we're using the direct internal + // interface here rather than that client-side wrapper infrastructure. + mHandler.post(() -> { + getContext().sendBroadcastAsUser(mTimeTickIntent, UserHandle.ALL); + + try { + callback.alarmComplete(this); + } catch (RemoteException e) { /* local method call */ } + }); + + synchronized (mLock) { + mLastTickReceived = mInjector.getCurrentTimeMillis(); + } + mClockReceiver.scheduleTimeTickEvent(); + } + }; + Intent intent = new Intent(Intent.ACTION_DATE_CHANGED); intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING | Intent.FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS); @@ -1438,12 +1464,9 @@ class AlarmManagerService extends SystemService { } } - void removeImpl(PendingIntent operation) { - if (operation == null) { - return; - } + void removeImpl(PendingIntent operation, IAlarmListener listener) { synchronized (mLock) { - removeLocked(operation, null); + removeLocked(operation, listener); } } @@ -1887,9 +1910,9 @@ class AlarmManagerService extends SystemService { pw.println(" App Standby Parole: " + mAppStandbyParole); pw.println(); - final long nowRTC = mInjector.getCurrentTimeMillis(); final long nowELAPSED = mInjector.getElapsedRealtime(); final long nowUPTIME = SystemClock.uptimeMillis(); + final long nowRTC = mInjector.getCurrentTimeMillis(); SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS"); pw.print(" nowRTC="); pw.print(nowRTC); @@ -1899,13 +1922,27 @@ class AlarmManagerService extends SystemService { pw.print(" mLastTimeChangeClockTime="); pw.print(mLastTimeChangeClockTime); pw.print("="); pw.println(sdf.format(new Date(mLastTimeChangeClockTime))); pw.print(" mLastTimeChangeRealtime="); pw.println(mLastTimeChangeRealtime); - pw.print(" mLastTickIssued="); - pw.println(sdf.format(new Date(nowRTC - (nowELAPSED - mLastTickIssued)))); pw.print(" mLastTickReceived="); pw.println(sdf.format(new Date(mLastTickReceived))); pw.print(" mLastTickSet="); pw.println(sdf.format(new Date(mLastTickSet))); pw.print(" mLastTickAdded="); pw.println(sdf.format(new Date(mLastTickAdded))); pw.print(" mLastTickRemoved="); pw.println(sdf.format(new Date(mLastTickRemoved))); + if (RECORD_ALARMS_IN_HISTORY) { + pw.println(); + pw.println(" Recent TIME_TICK history:"); + int i = mNextTickHistory; + do { + i--; + if (i < 0) i = TICK_HISTORY_DEPTH - 1; + final long time = mTickHistory[i]; + pw.print(" "); + pw.println((time > 0) + ? sdf.format(new Date(nowRTC - (nowELAPSED - time))) + : "-"); + } while (i != mNextTickHistory); + pw.println(); + } + SystemServiceManager ssm = LocalServices.getService(SystemServiceManager.class); if (ssm != null) { pw.println(); @@ -3640,8 +3677,8 @@ class AlarmManagerService extends SystemService { } // StatsLog requires currentTimeMillis(), which == nowRTC to within usecs. StatsLog.write(StatsLog.WALL_CLOCK_TIME_SHIFTED, nowRTC); - removeImpl(mTimeTickSender); - removeImpl(mDateChangeSender); + removeImpl(null, mTimeTickTrigger); + removeImpl(mDateChangeSender, null); rebatchAllAlarms(); mClockReceiver.scheduleTimeTickEvent(); mClockReceiver.scheduleDateChangedEvent(); @@ -3764,14 +3801,8 @@ class AlarmManagerService extends SystemService { void setWakelockWorkSource(PendingIntent pi, WorkSource ws, int type, String tag, int knownUid, boolean first) { try { - final boolean unimportant = pi == mTimeTickSender; - mWakeLock.setUnimportantForLogging(unimportant); - if (first || mLastWakeLockUnimportantForLogging) { - mWakeLock.setHistoryTag(tag); - } else { - mWakeLock.setHistoryTag(null); - } - mLastWakeLockUnimportantForLogging = unimportant; + mWakeLock.setHistoryTag(first ? tag : null); + if (ws != null) { mWakeLock.setWorkSource(ws); return; @@ -3828,7 +3859,7 @@ class AlarmManagerService extends SystemService { if (alarm.repeatInterval > 0) { // This IntentSender is no longer valid, but this // is a repeating alarm, so toss the hoser. - removeImpl(alarm.operation); + removeImpl(alarm.operation, null); } } } @@ -3886,22 +3917,13 @@ class AlarmManagerService extends SystemService { class ClockReceiver extends BroadcastReceiver { public ClockReceiver() { IntentFilter filter = new IntentFilter(); - filter.addAction(Intent.ACTION_TIME_TICK); filter.addAction(Intent.ACTION_DATE_CHANGED); getContext().registerReceiver(this, filter); } @Override public void onReceive(Context context, Intent intent) { - if (intent.getAction().equals(Intent.ACTION_TIME_TICK)) { - if (DEBUG_BATCH) { - Slog.v(TAG, "Received TIME_TICK alarm; rescheduling"); - } - synchronized (mLock) { - mLastTickReceived = mInjector.getCurrentTimeMillis(); - } - scheduleTimeTickEvent(); - } else if (intent.getAction().equals(Intent.ACTION_DATE_CHANGED)) { + if (intent.getAction().equals(Intent.ACTION_DATE_CHANGED)) { // Since the kernel does not keep track of DST, we need to // reset the TZ information at the beginning of each day // based off of the current Zone gmt offset + userspace tracked @@ -3923,7 +3945,7 @@ class AlarmManagerService extends SystemService { final WorkSource workSource = null; // Let system take blame for time tick events. setImpl(ELAPSED_REALTIME, mInjector.getElapsedRealtime() + tickEventDelay, 0, - 0, mTimeTickSender, null, null, AlarmManager.FLAG_STANDALONE, workSource, + 0, null, mTimeTickTrigger, null, AlarmManager.FLAG_STANDALONE, workSource, null, Process.myUid(), "android"); // Finally, remember when we set the tick alarm @@ -4333,10 +4355,6 @@ class AlarmManagerService extends SystemService { // PendingIntent alarm mSendCount++; - if (alarm.priorityClass.priority == PRIO_TICK) { - mLastTickIssued = nowELAPSED; - } - try { alarm.operation.send(getContext(), 0, mBackgroundIntent.putExtra( @@ -4344,13 +4362,10 @@ class AlarmManagerService extends SystemService { mDeliveryTracker, mHandler, null, allowWhileIdle ? mIdleOptions : null); } catch (PendingIntent.CanceledException e) { - if (alarm.operation == mTimeTickSender) { - Slog.wtf(TAG, "mTimeTickSender canceled"); - } if (alarm.repeatInterval > 0) { // This IntentSender is no longer valid, but this // is a repeating alarm, so toss it - removeImpl(alarm.operation); + removeImpl(alarm.operation, null); } // No actual delivery was possible, so the delivery tracker's // 'finished' callback won't be invoked. We also don't need @@ -4362,6 +4377,16 @@ class AlarmManagerService extends SystemService { } else { // Direct listener callback alarm mListenerCount++; + + if (RECORD_ALARMS_IN_HISTORY) { + if (alarm.listener == mTimeTickTrigger) { + mTickHistory[mNextTickHistory++] = nowELAPSED; + if (mNextTickHistory >= TICK_HISTORY_DEPTH) { + mNextTickHistory = 0; + } + } + } + try { if (DEBUG_LISTENER_CALLBACK) { Slog.v(TAG, "Alarm to uid=" + alarm.uid diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java index e41a09ef672e..953c99ff2474 100644 --- a/services/core/java/com/android/server/ConnectivityService.java +++ b/services/core/java/com/android/server/ConnectivityService.java @@ -57,6 +57,7 @@ import android.net.ConnectivityManager; import android.net.ConnectivityManager.PacketKeepalive; import android.net.IConnectivityManager; import android.net.IIpConnectivityMetrics; +import android.net.INetd; import android.net.INetdEventCallback; import android.net.INetworkManagementEventObserver; import android.net.INetworkPolicyListener; @@ -88,6 +89,7 @@ import android.net.metrics.IpConnectivityLog; import android.net.metrics.NetworkEvent; import android.net.netlink.InetDiagMessage; import android.net.util.MultinetworkPolicyTracker; +import android.net.util.NetdService; import android.os.Binder; import android.os.Build; import android.os.Bundle; @@ -259,7 +261,8 @@ public class ConnectivityService extends IConnectivityManager.Stub // 0 is full bad, 100 is full good private int mDefaultInetConditionPublished = 0; - private INetworkManagementService mNetd; + private INetworkManagementService mNMS; + private INetd mNetd; private INetworkStatsService mStatsService; private INetworkPolicyManager mPolicyManager; private NetworkPolicyManagerInternal mPolicyManagerInternal; @@ -390,9 +393,9 @@ public class ConnectivityService extends IConnectivityManager.Stub private static final int EVENT_PROMPT_UNVALIDATED = 29; /** - * used internally to (re)configure mobile data always-on settings. + * used internally to (re)configure always-on networks. */ - private static final int EVENT_CONFIGURE_MOBILE_DATA_ALWAYS_ON = 30; + private static final int EVENT_CONFIGURE_ALWAYS_ON_NETWORKS = 30; /** * used to add a network listener with a pending intent @@ -748,6 +751,12 @@ public class ConnectivityService extends IConnectivityManager.Stub mDefaultMobileDataRequest = createDefaultInternetRequestForTransport( NetworkCapabilities.TRANSPORT_CELLULAR, NetworkRequest.Type.BACKGROUND_REQUEST); + // The default WiFi request is a background request so that apps using WiFi are + // migrated to a better network (typically ethernet) when one comes up, instead + // of staying on WiFi forever. + mDefaultWifiRequest = createDefaultInternetRequestForTransport( + NetworkCapabilities.TRANSPORT_WIFI, NetworkRequest.Type.BACKGROUND_REQUEST); + mHandlerThread = new HandlerThread("ConnectivityServiceThread"); mHandlerThread.start(); mHandler = new InternalHandler(mHandlerThread.getLooper()); @@ -759,7 +768,7 @@ public class ConnectivityService extends IConnectivityManager.Stub mLingerDelayMs = mSystemProperties.getInt(LINGER_DELAY_PROPERTY, DEFAULT_LINGER_DELAY_MS); mContext = checkNotNull(context, "missing Context"); - mNetd = checkNotNull(netManager, "missing INetworkManagementService"); + mNMS = checkNotNull(netManager, "missing INetworkManagementService"); mStatsService = checkNotNull(statsService, "missing INetworkStatsService"); mPolicyManager = checkNotNull(policyManager, "missing INetworkPolicyManager"); mPolicyManagerInternal = checkNotNull( @@ -767,6 +776,7 @@ public class ConnectivityService extends IConnectivityManager.Stub "missing NetworkPolicyManagerInternal"); mProxyTracker = new ProxyTracker(context, mHandler, EVENT_PROXY_HAS_CHANGED); + mNetd = NetdService.getInstance(); mKeyStore = KeyStore.getInstance(); mTelephonyManager = (TelephonyManager) mContext.getSystemService(Context.TELEPHONY_SERVICE); @@ -849,7 +859,7 @@ public class ConnectivityService extends IConnectivityManager.Stub mTethering = makeTethering(); - mPermissionMonitor = new PermissionMonitor(mContext, mNetd); + mPermissionMonitor = new PermissionMonitor(mContext, mNMS); //set up the listener for user state for creating user VPNs IntentFilter intentFilter = new IntentFilter(); @@ -864,8 +874,8 @@ public class ConnectivityService extends IConnectivityManager.Stub new IntentFilter(Intent.ACTION_USER_PRESENT), null, null); try { - mNetd.registerObserver(mTethering); - mNetd.registerObserver(mDataActivityObserver); + mNMS.registerObserver(mTethering); + mNMS.registerObserver(mDataActivityObserver); } catch (RemoteException e) { loge("Error registering observer :" + e); } @@ -896,7 +906,7 @@ public class ConnectivityService extends IConnectivityManager.Stub mMultipathPolicyTracker = new MultipathPolicyTracker(mContext, mHandler); - mDnsManager = new DnsManager(mContext, mNetd, mSystemProperties); + mDnsManager = new DnsManager(mContext, mNMS, mSystemProperties); registerPrivateDnsSettingsCallbacks(); } @@ -912,7 +922,7 @@ public class ConnectivityService extends IConnectivityManager.Stub return mDefaultRequest; } }; - return new Tethering(mContext, mNetd, mStatsService, mPolicyManager, + return new Tethering(mContext, mNMS, mStatsService, mPolicyManager, IoThread.get().getLooper(), new MockableSystemProperties(), deps); } @@ -944,8 +954,8 @@ public class ConnectivityService extends IConnectivityManager.Stub // 2. Give FakeSettingsProvider an alternative notification mechanism and have the test use it // by subclassing SettingsObserver. @VisibleForTesting - void updateMobileDataAlwaysOn() { - mHandler.sendEmptyMessage(EVENT_CONFIGURE_MOBILE_DATA_ALWAYS_ON); + void updateAlwaysOnNetworks() { + mHandler.sendEmptyMessage(EVENT_CONFIGURE_ALWAYS_ON_NETWORKS); } // See FakeSettingsProvider comment above. @@ -954,22 +964,30 @@ public class ConnectivityService extends IConnectivityManager.Stub mHandler.sendEmptyMessage(EVENT_PRIVATE_DNS_SETTINGS_CHANGED); } - private void handleMobileDataAlwaysOn() { + private void handleAlwaysOnNetworkRequest( + NetworkRequest networkRequest, String settingName, boolean defaultValue) { final boolean enable = toBool(Settings.Global.getInt( - mContext.getContentResolver(), Settings.Global.MOBILE_DATA_ALWAYS_ON, 1)); - final boolean isEnabled = (mNetworkRequests.get(mDefaultMobileDataRequest) != null); + mContext.getContentResolver(), settingName, encodeBool(defaultValue))); + final boolean isEnabled = (mNetworkRequests.get(networkRequest) != null); if (enable == isEnabled) { return; // Nothing to do. } if (enable) { handleRegisterNetworkRequest(new NetworkRequestInfo( - null, mDefaultMobileDataRequest, new Binder())); + null, networkRequest, new Binder())); } else { - handleReleaseNetworkRequest(mDefaultMobileDataRequest, Process.SYSTEM_UID); + handleReleaseNetworkRequest(networkRequest, Process.SYSTEM_UID); } } + private void handleConfigureAlwaysOnNetworks() { + handleAlwaysOnNetworkRequest( + mDefaultMobileDataRequest,Settings.Global.MOBILE_DATA_ALWAYS_ON, true); + handleAlwaysOnNetworkRequest(mDefaultWifiRequest, Settings.Global.WIFI_ALWAYS_REQUESTED, + false); + } + private void registerSettingsCallbacks() { // Watch for global HTTP proxy changes. mSettingsObserver.observe( @@ -979,7 +997,12 @@ public class ConnectivityService extends IConnectivityManager.Stub // Watch for whether or not to keep mobile data always on. mSettingsObserver.observe( Settings.Global.getUriFor(Settings.Global.MOBILE_DATA_ALWAYS_ON), - EVENT_CONFIGURE_MOBILE_DATA_ALWAYS_ON); + EVENT_CONFIGURE_ALWAYS_ON_NETWORKS); + + // Watch for whether or not to keep wifi always on. + mSettingsObserver.observe( + Settings.Global.getUriFor(Settings.Global.WIFI_ALWAYS_REQUESTED), + EVENT_CONFIGURE_ALWAYS_ON_NETWORKS); } private void registerPrivateDnsSettingsCallbacks() { @@ -1476,6 +1499,20 @@ public class ConnectivityService extends IConnectivityManager.Stub }; /** + * Ensures that the system cannot call a particular method. + */ + private boolean disallowedBecauseSystemCaller() { + // TODO: start throwing a SecurityException when GnssLocationProvider stops calling + // requestRouteToHost. + if (isSystem(Binder.getCallingUid())) { + log("This method exists only for app backwards compatibility" + + " and must not be called by system services."); + return true; + } + return false; + } + + /** * Ensure that a network route exists to deliver traffic to the specified * host via the specified network interface. * @param networkType the type of the network over which traffic to the @@ -1486,6 +1523,9 @@ public class ConnectivityService extends IConnectivityManager.Stub */ @Override public boolean requestRouteToHostAddress(int networkType, byte[] hostAddress) { + if (disallowedBecauseSystemCaller()) { + return false; + } enforceChangePermission(); if (mProtectedNetworks.contains(networkType)) { enforceConnectivityInternalPermission(); @@ -1563,7 +1603,7 @@ public class ConnectivityService extends IConnectivityManager.Stub if (DBG) log("Adding legacy route " + bestRoute + " for UID/PID " + uid + "/" + Binder.getCallingPid()); try { - mNetd.addLegacyRouteForNetId(netId, bestRoute, uid); + mNMS.addLegacyRouteForNetId(netId, bestRoute, uid); } catch (Exception e) { // never crash - catch them all if (DBG) loge("Exception trying to add a route: " + e); @@ -1814,8 +1854,8 @@ public class ConnectivityService extends IConnectivityManager.Stub // for user to unlock device too. updateLockdownVpn(); - // Configure whether mobile data is always on. - mHandler.sendMessage(mHandler.obtainMessage(EVENT_CONFIGURE_MOBILE_DATA_ALWAYS_ON)); + // Create network requests for always-on networks. + mHandler.sendMessage(mHandler.obtainMessage(EVENT_CONFIGURE_ALWAYS_ON_NETWORKS)); mHandler.sendMessage(mHandler.obtainMessage(EVENT_SYSTEM_READY)); @@ -1853,7 +1893,7 @@ public class ConnectivityService extends IConnectivityManager.Stub if (timeout > 0 && iface != null && type != ConnectivityManager.TYPE_NONE) { try { - mNetd.addIdleTimer(iface, timeout, type); + mNMS.addIdleTimer(iface, timeout, type); } catch (Exception e) { // You shall not crash! loge("Exception in setupDataActivityTracking " + e); @@ -1872,7 +1912,7 @@ public class ConnectivityService extends IConnectivityManager.Stub caps.hasTransport(NetworkCapabilities.TRANSPORT_WIFI))) { try { // the call fails silently if no idle timer setup for this interface - mNetd.removeIdleTimer(iface); + mNMS.removeIdleTimer(iface); } catch (Exception e) { loge("Exception in removeDataActivityTracking " + e); } @@ -1880,6 +1920,18 @@ public class ConnectivityService extends IConnectivityManager.Stub } /** + * Update data activity tracking when network state is updated. + */ + private void updateDataActivityTracking(NetworkAgentInfo newNetwork, + NetworkAgentInfo oldNetwork) { + if (newNetwork != null) { + setupDataActivityTracking(newNetwork); + } + if (oldNetwork != null) { + removeDataActivityTracking(oldNetwork); + } + } + /** * Reads the network specific MTU size from resources. * and set it on it's iface. */ @@ -1907,7 +1959,7 @@ public class ConnectivityService extends IConnectivityManager.Stub try { if (VDBG) log("Setting MTU size: " + iface + ", " + mtu); - mNetd.setMtu(iface, mtu); + mNMS.setMtu(iface, mtu); } catch (Exception e) { Slog.e(TAG, "exception in setMtu()" + e); } @@ -2561,7 +2613,7 @@ public class ConnectivityService extends IConnectivityManager.Stub } nai.clearLingerState(); if (nai.isSatisfyingRequest(mDefaultRequest.requestId)) { - removeDataActivityTracking(nai); + updateDataActivityTracking(null /* newNetwork */, nai); notifyLockdownVpn(nai); ensureNetworkTransitionWakelock(nai.name()); } @@ -2581,7 +2633,7 @@ public class ConnectivityService extends IConnectivityManager.Stub // NetworkFactories, so network traffic isn't interrupted for an unnecessarily // long time. try { - mNetd.removeNetwork(nai.network.netId); + mNMS.removeNetwork(nai.network.netId); } catch (Exception e) { loge("Exception removing network: " + e); } @@ -2779,20 +2831,6 @@ public class ConnectivityService extends IConnectivityManager.Stub } } - // TODO: remove this code once we know that the Slog.wtf is never hit. - // - // Find all networks that are satisfying this request and remove the request - // from their request lists. - // TODO - it's my understanding that for a request there is only a single - // network satisfying it, so this loop is wasteful - for (NetworkAgentInfo otherNai : mNetworkAgentInfos.values()) { - if (otherNai.isSatisfyingRequest(nri.request.requestId) && otherNai != nai) { - Slog.wtf(TAG, "Request " + nri.request + " satisfied by " + - otherNai.name() + ", but mNetworkAgentInfos says " + - (nai != null ? nai.name() : "null")); - } - } - // Maintain the illusion. When this request arrived, we might have pretended // that a network connected to serve it, even though the network was already // connected. Now that this request has gone away, we might have to pretend @@ -3106,8 +3144,8 @@ public class ConnectivityService extends IConnectivityManager.Stub handlePromptUnvalidated((Network) msg.obj); break; } - case EVENT_CONFIGURE_MOBILE_DATA_ALWAYS_ON: { - handleMobileDataAlwaysOn(); + case EVENT_CONFIGURE_ALWAYS_ON_NETWORKS: { + handleConfigureAlwaysOnNetworks(); break; } // Sent by KeepaliveTracker to process an app request on the state machine thread. @@ -3760,7 +3798,7 @@ public class ConnectivityService extends IConnectivityManager.Stub Slog.w(TAG, "VPN for user " + user + " not ready yet. Skipping lockdown"); return false; } - setLockdownTracker(new LockdownVpnTracker(mContext, mNetd, this, vpn, profile)); + setLockdownTracker(new LockdownVpnTracker(mContext, mNMS, this, vpn, profile)); } else { setLockdownTracker(null); } @@ -4015,7 +4053,7 @@ public class ConnectivityService extends IConnectivityManager.Stub loge("Starting user already has a VPN"); return; } - userVpn = new Vpn(mHandler.getLooper(), mContext, mNetd, userId); + userVpn = new Vpn(mHandler.getLooper(), mContext, mNMS, userId); mVpns.put(userId, userVpn); if (mUserManager.getUserInfo(userId).isPrimary() && LockdownVpnTracker.isEnabled()) { updateLockdownVpn(); @@ -4535,6 +4573,10 @@ public class ConnectivityService extends IConnectivityManager.Stub // priority networks like Wi-Fi are active. private final NetworkRequest mDefaultMobileDataRequest; + // Request used to optionally keep wifi data active even when higher + // priority networks like ethernet are active. + private final NetworkRequest mDefaultWifiRequest; + private NetworkAgentInfo getNetworkForRequest(int requestId) { synchronized (mNetworkForRequestId) { return mNetworkForRequestId.get(requestId); @@ -4632,7 +4674,7 @@ public class ConnectivityService extends IConnectivityManager.Stub mDnsManager.updatePrivateDnsStatus(netId, newLp); // Start or stop clat accordingly to network state. - networkAgent.updateClat(mNetd); + networkAgent.updateClat(mNMS); if (isDefaultNetwork(networkAgent)) { handleApplyDefaultProxy(newLp.getHttpProxy()); } else { @@ -4671,9 +4713,9 @@ public class ConnectivityService extends IConnectivityManager.Stub final String prefix = "iface:" + iface; try { if (add) { - mNetd.getNetdService().wakeupAddInterface(iface, prefix, mark, mask); + mNetd.wakeupAddInterface(iface, prefix, mark, mask); } else { - mNetd.getNetdService().wakeupDelInterface(iface, prefix, mark, mask); + mNetd.wakeupDelInterface(iface, prefix, mark, mask); } } catch (Exception e) { loge("Exception modifying wakeup packet monitoring: " + e); @@ -4689,7 +4731,7 @@ public class ConnectivityService extends IConnectivityManager.Stub for (String iface : interfaceDiff.added) { try { if (DBG) log("Adding iface " + iface + " to network " + netId); - mNetd.addInterfaceToNetwork(iface, netId); + mNMS.addInterfaceToNetwork(iface, netId); wakeupModifyInterface(iface, caps, true); } catch (Exception e) { loge("Exception adding interface: " + e); @@ -4699,7 +4741,7 @@ public class ConnectivityService extends IConnectivityManager.Stub try { if (DBG) log("Removing iface " + iface + " from network " + netId); wakeupModifyInterface(iface, caps, false); - mNetd.removeInterfaceFromNetwork(iface, netId); + mNMS.removeInterfaceFromNetwork(iface, netId); } catch (Exception e) { loge("Exception removing interface: " + e); } @@ -4723,7 +4765,7 @@ public class ConnectivityService extends IConnectivityManager.Stub if (route.hasGateway()) continue; if (VDBG) log("Adding Route [" + route + "] to network " + netId); try { - mNetd.addRoute(netId, route); + mNMS.addRoute(netId, route); } catch (Exception e) { if ((route.getDestination().getAddress() instanceof Inet4Address) || VDBG) { loge("Exception in addRoute for non-gateway: " + e); @@ -4734,7 +4776,7 @@ public class ConnectivityService extends IConnectivityManager.Stub if (route.hasGateway() == false) continue; if (VDBG) log("Adding Route [" + route + "] to network " + netId); try { - mNetd.addRoute(netId, route); + mNMS.addRoute(netId, route); } catch (Exception e) { if ((route.getGateway() instanceof Inet4Address) || VDBG) { loge("Exception in addRoute for gateway: " + e); @@ -4745,7 +4787,7 @@ public class ConnectivityService extends IConnectivityManager.Stub for (RouteInfo route : routeDiff.removed) { if (VDBG) log("Removing Route [" + route + "] from network " + netId); try { - mNetd.removeRoute(netId, route); + mNMS.removeRoute(netId, route); } catch (Exception e) { loge("Exception in removeRoute: " + e); } @@ -4857,7 +4899,7 @@ public class ConnectivityService extends IConnectivityManager.Stub final String newPermission = getNetworkPermission(newNc); if (!Objects.equals(oldPermission, newPermission) && nai.created && !nai.isVPN()) { try { - mNetd.setNetworkPermission(nai.network.netId, newPermission); + mNMS.setNetworkPermission(nai.network.netId, newPermission); } catch (RemoteException e) { loge("Exception in setNetworkPermission: " + e); } @@ -4917,12 +4959,12 @@ public class ConnectivityService extends IConnectivityManager.Stub if (!newRanges.isEmpty()) { final UidRange[] addedRangesArray = new UidRange[newRanges.size()]; newRanges.toArray(addedRangesArray); - mNetd.addVpnUidRanges(nai.network.netId, addedRangesArray); + mNMS.addVpnUidRanges(nai.network.netId, addedRangesArray); } if (!prevRanges.isEmpty()) { final UidRange[] removedRangesArray = new UidRange[prevRanges.size()]; prevRanges.toArray(removedRangesArray); - mNetd.removeVpnUidRanges(nai.network.netId, removedRangesArray); + mNMS.removeVpnUidRanges(nai.network.netId, removedRangesArray); } } catch (Exception e) { // Never crash! @@ -5091,9 +5133,9 @@ public class ConnectivityService extends IConnectivityManager.Stub private void makeDefault(NetworkAgentInfo newNetwork) { if (DBG) log("Switching to new default network: " + newNetwork); - setupDataActivityTracking(newNetwork); + try { - mNetd.setDefaultNetId(newNetwork.network.netId); + mNMS.setDefaultNetId(newNetwork.network.netId); } catch (Exception e) { loge("Exception setting default network :" + e); } @@ -5266,6 +5308,7 @@ public class ConnectivityService extends IConnectivityManager.Stub } } if (isNewDefault) { + updateDataActivityTracking(newNetwork, oldDefaultNetwork); // Notify system services that this network is up. makeDefault(newNetwork); // Log 0 -> X and Y -> X default network transitions, where X is the new default. @@ -5488,12 +5531,12 @@ public class ConnectivityService extends IConnectivityManager.Stub try { // This should never fail. Specifying an already in use NetID will cause failure. if (networkAgent.isVPN()) { - mNetd.createVirtualNetwork(networkAgent.network.netId, + mNMS.createVirtualNetwork(networkAgent.network.netId, !networkAgent.linkProperties.getDnsServers().isEmpty(), (networkAgent.networkMisc == null || !networkAgent.networkMisc.allowBypass)); } else { - mNetd.createPhysicalNetwork(networkAgent.network.netId, + mNMS.createPhysicalNetwork(networkAgent.network.netId, getNetworkPermission(networkAgent.networkCapabilities)); } } catch (Exception e) { diff --git a/services/core/java/com/android/server/LooperStatsService.java b/services/core/java/com/android/server/LooperStatsService.java index 4f0e17055769..96ce6a4ee6a4 100644 --- a/services/core/java/com/android/server/LooperStatsService.java +++ b/services/core/java/com/android/server/LooperStatsService.java @@ -129,7 +129,12 @@ public class LooperStatsService extends Binder { } private void setSamplingInterval(int samplingInterval) { - mStats.setSamplingInterval(samplingInterval); + if (samplingInterval > 0) { + mStats.setSamplingInterval(samplingInterval); + } else { + Slog.w(TAG, "Ignored invalid sampling interval (value must be positive): " + + samplingInterval); + } } /** diff --git a/services/core/java/com/android/server/NetworkManagementService.java b/services/core/java/com/android/server/NetworkManagementService.java index 1d163eed00a4..de930f794e50 100644 --- a/services/core/java/com/android/server/NetworkManagementService.java +++ b/services/core/java/com/android/server/NetworkManagementService.java @@ -161,6 +161,8 @@ public class NetworkManagementService extends INetworkManagementService.Stub private static final int MAX_UID_RANGES_PER_COMMAND = 10; + private static final String[] EMPTY_STRING_ARRAY = new String[0]; + /** * Name representing {@link #setGlobalAlert(long)} limit when delivered to * {@link INetworkManagementEventObserver#limitReached(String, String)}. @@ -1234,18 +1236,12 @@ public class NetworkManagementService extends INetworkManagementService.Stub @Override public void startTethering(String[] dhcpRange) { mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG); - // cmd is "tether start first_start first_stop second_start second_stop ..." // an odd number of addrs will fail - final Command cmd = new Command("tether", "start"); - for (String d : dhcpRange) { - cmd.appendArg(d); - } - try { - mConnector.execute(cmd); - } catch (NativeDaemonConnectorException e) { - throw e.rethrowAsParcelableException(); + mNetdService.tetherStart(dhcpRange); + } catch (RemoteException | ServiceSpecificException e) { + throw new IllegalStateException(e); } } @@ -1253,9 +1249,9 @@ public class NetworkManagementService extends INetworkManagementService.Stub public void stopTethering() { mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG); try { - mConnector.execute("tether", "stop"); - } catch (NativeDaemonConnectorException e) { - throw e.rethrowAsParcelableException(); + mNetdService.tetherStop(); + } catch (RemoteException | ServiceSpecificException e) { + throw new IllegalStateException(e); } } @@ -1263,25 +1259,21 @@ public class NetworkManagementService extends INetworkManagementService.Stub public boolean isTetheringStarted() { mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG); - final NativeDaemonEvent event; try { - event = mConnector.execute("tether", "status"); - } catch (NativeDaemonConnectorException e) { - throw e.rethrowAsParcelableException(); + final boolean isEnabled = mNetdService.tetherIsEnabled(); + return isEnabled; + } catch (RemoteException | ServiceSpecificException e) { + throw new IllegalStateException(e); } - - // 210 Tethering services started - event.checkCode(TetherStatusResult); - return event.getMessage().endsWith("started"); } @Override public void tetherInterface(String iface) { mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG); try { - mConnector.execute("tether", "interface", "add", iface); - } catch (NativeDaemonConnectorException e) { - throw e.rethrowAsParcelableException(); + mNetdService.tetherInterfaceAdd(iface); + } catch (RemoteException | ServiceSpecificException e) { + throw new IllegalStateException(e); } List<RouteInfo> routes = new ArrayList<>(); // The RouteInfo constructor truncates the LinkAddress to a network prefix, thus making it @@ -1294,9 +1286,9 @@ public class NetworkManagementService extends INetworkManagementService.Stub public void untetherInterface(String iface) { mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG); try { - mConnector.execute("tether", "interface", "remove", iface); - } catch (NativeDaemonConnectorException e) { - throw e.rethrowAsParcelableException(); + mNetdService.tetherInterfaceRemove(iface); + } catch (RemoteException | ServiceSpecificException e) { + throw new IllegalStateException(e); } finally { removeInterfaceFromLocalNetwork(iface); } @@ -1306,11 +1298,10 @@ public class NetworkManagementService extends INetworkManagementService.Stub public String[] listTetheredInterfaces() { mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG); try { - return NativeDaemonEvent.filterMessageList( - mConnector.executeForList("tether", "interface", "list"), - TetherInterfaceListResult); - } catch (NativeDaemonConnectorException e) { - throw e.rethrowAsParcelableException(); + final List<String> result = mNetdService.tetherInterfaceList(); + return result.toArray(EMPTY_STRING_ARRAY); + } catch (RemoteException | ServiceSpecificException e) { + throw new IllegalStateException(e); } } @@ -1319,16 +1310,11 @@ public class NetworkManagementService extends INetworkManagementService.Stub mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG); int netId = (network != null) ? network.netId : ConnectivityManager.NETID_UNSET; - final Command cmd = new Command("tether", "dns", "set", netId); - - for (String s : dns) { - cmd.appendArg(NetworkUtils.numericToInetAddress(s).getHostAddress()); - } try { - mConnector.execute(cmd); - } catch (NativeDaemonConnectorException e) { - throw e.rethrowAsParcelableException(); + mNetdService.tetherDnsSet(netId, dns); + } catch (RemoteException | ServiceSpecificException e) { + throw new IllegalStateException(e); } } @@ -1336,10 +1322,10 @@ public class NetworkManagementService extends INetworkManagementService.Stub public String[] getDnsForwarders() { mContext.enforceCallingOrSelfPermission(CONNECTIVITY_INTERNAL, TAG); try { - return NativeDaemonEvent.filterMessageList( - mConnector.executeForList("tether", "dns", "list"), TetherDnsFwdTgtListResult); - } catch (NativeDaemonConnectorException e) { - throw e.rethrowAsParcelableException(); + final List<String> result = mNetdService.tetherDnsList(); + return result.toArray(EMPTY_STRING_ARRAY); + } catch (RemoteException | ServiceSpecificException e) { + throw new IllegalStateException(e); } } diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java index 98b88cb557af..fb8894b48411 100644 --- a/services/core/java/com/android/server/TelephonyRegistry.java +++ b/services/core/java/com/android/server/TelephonyRegistry.java @@ -213,6 +213,8 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { private PhoneCapability mPhoneCapability = null; + private int mPreferredDataSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID; + private final LocalLog mLocalLog = new LocalLog(100); private PreciseDataConnectionState mPreciseDataConnectionState = @@ -752,6 +754,13 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { remove(r.binder); } } + if ((events & PhoneStateListener.LISTEN_PREFERRED_DATA_SUBID_CHANGE) != 0) { + try { + r.callback.onPreferredDataSubIdChanged(mPreferredDataSubId); + } catch (RemoteException ex) { + remove(r.binder); + } + } } } } else { @@ -1573,6 +1582,31 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { } } + public void notifyPreferredDataSubIdChanged(int preferredSubId) { + if (!checkNotifyPermission("notifyPreferredDataSubIdChanged()")) { + return; + } + + if (VDBG) { + log("notifyPreferredDataSubIdChanged: preferredSubId=" + preferredSubId); + } + + synchronized (mRecords) { + mPreferredDataSubId = preferredSubId; + + for (Record r : mRecords) { + if (r.matchPhoneStateListenerEvent( + PhoneStateListener.LISTEN_PREFERRED_DATA_SUBID_CHANGE)) { + try { + r.callback.onPreferredDataSubIdChanged(preferredSubId); + } catch (RemoteException ex) { + mRemoveList.add(r.binder); + } + } + } + handleRemoveListLocked(); + } + } @Override public void dump(FileDescriptor fd, PrintWriter writer, String[] args) { @@ -1610,6 +1644,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { pw.println("mBackgroundCallState=" + mBackgroundCallState); pw.println("mVoLteServiceState=" + mVoLteServiceState); pw.println("mPhoneCapability=" + mPhoneCapability); + pw.println("mPreferredDataSubId=" + mPreferredDataSubId); pw.decreaseIndent(); @@ -1647,6 +1682,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { intent.putExtras(data); // Pass the subscription along with the intent. intent.putExtra(PhoneConstants.SUBSCRIPTION_KEY, subId); + intent.putExtra(SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX, subId); intent.putExtra(PhoneConstants.SLOT_KEY, phoneId); mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL); } @@ -1701,6 +1737,7 @@ public class TelephonyRegistry extends ITelephonyRegistry.Stub { if (subId != SubscriptionManager.INVALID_SUBSCRIPTION_ID) { intent.setAction(PhoneConstants.ACTION_SUBSCRIPTION_PHONE_STATE_CHANGED); intent.putExtra(PhoneConstants.SUBSCRIPTION_KEY, subId); + intent.putExtra(SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX, subId); } // If the phoneId is invalid, the broadcast is for overall call state. if (phoneId != SubscriptionManager.INVALID_PHONE_INDEX) { diff --git a/services/core/java/com/android/server/Watchdog.java b/services/core/java/com/android/server/Watchdog.java index 6d69fcd3e453..0b836f0d186f 100644 --- a/services/core/java/com/android/server/Watchdog.java +++ b/services/core/java/com/android/server/Watchdog.java @@ -94,7 +94,7 @@ public class Watchdog extends Thread { "media.metrics", // system/bin/mediametrics "media.codec", // vendor/bin/hw/android.hardware.media.omx@1.0-service "com.android.bluetooth", // Bluetooth service - "statsd", // Stats daemon + "/system/bin/statsd", // Stats daemon }; public static final List<String> HAL_INTERFACES_OF_INTEREST = Arrays.asList( diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index d670bf1b387c..aa14da0ad70a 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -2397,6 +2397,9 @@ public class ActivityManagerService extends IActivityManager.Stub mUserController = new UserController(this); + mPendingIntentController = new PendingIntentController( + mHandlerThread.getLooper(), mUserController); + GL_ES_VERSION = SystemProperties.getInt("ro.opengles.version", ConfigurationInfo.GL_ES_VERSION_UNDEFINED); @@ -2412,9 +2415,6 @@ public class ActivityManagerService extends IActivityManager.Stub mAtmInternal = LocalServices.getService(ActivityTaskManagerInternal.class); mStackSupervisor = mActivityTaskManager.mStackSupervisor; - mPendingIntentController = new PendingIntentController( - mHandlerThread.getLooper(), mUserController); - mProcessCpuThread = new Thread("CpuTracker") { @Override public void run() { @@ -4202,7 +4202,6 @@ public class ActivityManagerService extends IActivityManager.Stub private final void handleAppDiedLocked(ProcessRecord app, boolean restarting, boolean allowRestart) { int pid = app.pid; - final boolean clearLaunchStartTime = !restarting && app.removed && app.foregroundActivities; boolean kept = cleanUpApplicationRecordLocked(app, restarting, allowRestart, -1, false /*replacingPid*/); if (!kept && !restarting) { @@ -4243,18 +4242,6 @@ public class ActivityManagerService extends IActivityManager.Stub mWindowManager.continueSurfaceLayout(); } - // TODO (b/67683350) - // When an app process is removed, activities from the process may be relaunched. In the - // case of forceStopPackageLocked the activities are finished before any window is drawn, - // and the launch time is not cleared. This will be incorrectly used to calculate launch - // time for the next launched activity launched in the same windowing mode. - if (clearLaunchStartTime) { - final LaunchTimeTracker.Entry entry = mStackSupervisor - .getLaunchTimeTracker().getEntry(mStackSupervisor.getWindowingMode()); - if (entry != null) { - entry.mLaunchStartTime = 0; - } - } } private final int getLRURecordIndexForAppLocked(IApplicationThread thread) { @@ -7641,7 +7628,23 @@ public class ActivityManagerService extends IActivityManager.Stub } } - boolean providerRunning = cpr != null && cpr.proc != null && !cpr.proc.killed; + boolean providerRunning = false; + + if (cpr != null && cpr.proc != null) { + providerRunning = !cpr.proc.killed; + + // Note if killedByAm is also set, this means the provider process has just been + // killed by AM (in ProcessRecord.kill()), but appDiedLocked() hasn't been called + // yet. So we need to call appDiedLocked() here and let it clean up. + // (See the commit message on I2c4ba1e87c2d47f2013befff10c49b3dc337a9a7 to see + // how to test this case.) + if (cpr.proc.killed && cpr.proc.killedByAm) { + checkTime(startTime, "getContentProviderImpl: before appDied (killedByAm)"); + appDiedLocked(cpr.proc); + checkTime(startTime, "getContentProviderImpl: after appDied (killedByAm)"); + } + } + if (providerRunning) { cpi = cpr.info; String msg; diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java index 4bcaf7145e60..40c555f8c2e6 100644 --- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java +++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java @@ -568,9 +568,6 @@ final class ActivityManagerShellCommand extends ShellCommand { if (result.who != null) { pw.println("Activity: " + result.who.flattenToShortString()); } - if (result.thisTime >= 0) { - pw.println("ThisTime: " + result.thisTime); - } if (result.totalTime >= 0) { pw.println("TotalTime: " + result.totalTime); } diff --git a/services/core/java/com/android/server/am/ActivityMetricsLogger.java b/services/core/java/com/android/server/am/ActivityMetricsLogger.java index 78b42f2068ee..18cdb054e648 100644 --- a/services/core/java/com/android/server/am/ActivityMetricsLogger.java +++ b/services/core/java/com/android/server/am/ActivityMetricsLogger.java @@ -75,6 +75,7 @@ import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.TYPE_T import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_METRICS; import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM; import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME; +import static com.android.server.am.EventLogTags.AM_ACTIVITY_LAUNCH_TIME; import static com.android.server.am.MemoryStatUtil.MemoryStat; import static com.android.server.am.MemoryStatUtil.readMemoryStatFromFilesystem; import static com.android.server.wm.ActivityTaskManagerInternal.APP_TRANSITION_TIMEOUT; @@ -89,10 +90,14 @@ import android.os.Handler; import android.os.Looper; import android.os.Message; import android.os.SystemClock; +import android.os.Trace; +import android.util.EventLog; +import android.util.Log; import android.util.Slog; import android.util.SparseArray; import android.util.SparseIntArray; import android.util.StatsLog; +import android.util.TimeUtils; import com.android.internal.logging.MetricsLogger; import com.android.internal.os.BackgroundThread; @@ -100,7 +105,12 @@ import com.android.internal.os.SomeArgs; import com.android.server.LocalServices; /** - * Handles logging into Tron. + * Listens to activity launches, transitions, visibility changes and window drawn callbacks to + * determine app launch times and draw delays. Source of truth for activity metrics and provides + * data for Tron, logcat, event logs and {@link android.app.WaitResult}. + * + * Tests: + * atest SystemMetricsFunctionalTests */ class ActivityMetricsLogger { @@ -115,6 +125,8 @@ class ActivityMetricsLogger { private static final int WINDOW_STATE_INVALID = -1; private static final long INVALID_START_TIME = -1; + private static final int INVALID_DELAY = -1; + private static final int INVALID_TRANSITION_TYPE = -1; private static final int MSG_CHECK_VISIBILITY = 0; @@ -143,6 +155,8 @@ class ActivityMetricsLogger { private final H mHandler; private ArtManagerInternal mArtManagerInternal; + private boolean mDrawingTraceActive; + private final StringBuilder mStringBuilder = new StringBuilder(); private final class H extends Handler { @@ -165,36 +179,56 @@ class ActivityMetricsLogger { private ActivityRecord launchedActivity; private int startResult; private boolean currentTransitionProcessRunning; + /** Elapsed time from when we launch an activity to when its windows are drawn. */ private int windowsDrawnDelayMs; - private int startingWindowDelayMs = -1; - private int bindApplicationDelayMs = -1; + private int startingWindowDelayMs = INVALID_DELAY; + private int bindApplicationDelayMs = INVALID_DELAY; private int reason = APP_TRANSITION_TIMEOUT; private boolean loggedWindowsDrawn; private boolean loggedStartingWindowDrawn; + private boolean launchTraceActive; } - private final class WindowingModeTransitionInfoSnapshot { + final class WindowingModeTransitionInfoSnapshot { final private ApplicationInfo applicationInfo; final private WindowProcessController processRecord; - final private String packageName; - final private String launchedActivityName; + final String packageName; + final String launchedActivityName; final private String launchedActivityLaunchedFromPackage; final private String launchedActivityLaunchToken; final private String launchedActivityAppRecordRequiredAbi; + final String launchedActivityShortComponentName; final private String processName; final private int reason; final private int startingWindowDelayMs; final private int bindApplicationDelayMs; - final private int windowsDrawnDelayMs; - final private int type; + final int windowsDrawnDelayMs; + final int type; + final int userId; + /** + * Elapsed time from when we launch an activity to when the app reported it was + * fully drawn. If this is not reported then the value is set to INVALID_DELAY. + */ + final int windowsFullyDrawnDelayMs; + final int activityRecordIdHashCode; private WindowingModeTransitionInfoSnapshot(WindowingModeTransitionInfo info) { - applicationInfo = info.launchedActivity.appInfo; - packageName = info.launchedActivity.packageName; - launchedActivityName = info.launchedActivity.info.name; - launchedActivityLaunchedFromPackage = info.launchedActivity.launchedFromPackage; - launchedActivityLaunchToken = info.launchedActivity.info.launchToken; - launchedActivityAppRecordRequiredAbi = info.launchedActivity.app == null + this(info, info.launchedActivity); + } + + private WindowingModeTransitionInfoSnapshot(WindowingModeTransitionInfo info, + ActivityRecord launchedActivity) { + this(info, launchedActivity, INVALID_DELAY); + } + + private WindowingModeTransitionInfoSnapshot(WindowingModeTransitionInfo info, + ActivityRecord launchedActivity, int windowsFullyDrawnDelayMs) { + applicationInfo = launchedActivity.appInfo; + packageName = launchedActivity.packageName; + launchedActivityName = launchedActivity.info.name; + launchedActivityLaunchedFromPackage = launchedActivity.launchedFromPackage; + launchedActivityLaunchToken = launchedActivity.info.launchToken; + launchedActivityAppRecordRequiredAbi = launchedActivity.app == null ? null : info.launchedActivity.app.getRequiredAbi(); reason = info.reason; @@ -204,6 +238,10 @@ class ActivityMetricsLogger { type = getTransitionType(info); processRecord = findProcessForActivity(info.launchedActivity); processName = info.launchedActivity.processName; + userId = launchedActivity.userId; + launchedActivityShortComponentName = launchedActivity.shortComponentName; + activityRecordIdHashCode = System.identityHashCode(launchedActivity); + this.windowsFullyDrawnDelayMs = windowsFullyDrawnDelayMs; } } @@ -335,7 +373,7 @@ class ActivityMetricsLogger { || windowingMode == WINDOWING_MODE_UNDEFINED) && !otherWindowModesLaunching) { // Failed to launch or it was not a process switch, so we don't care about the timing. - reset(true /* abort */); + reset(true /* abort */, info); return; } else if (otherWindowModesLaunching) { // Don't log this windowing mode but continue with the other windowing modes. @@ -351,6 +389,7 @@ class ActivityMetricsLogger { mWindowingModeTransitionInfo.put(windowingMode, newInfo); mLastWindowingModeTransitionInfo.put(windowingMode, newInfo); mCurrentTransitionDeviceUptime = (int) (SystemClock.uptimeMillis() / 1000); + startTraces(newInfo); } /** @@ -364,18 +403,21 @@ class ActivityMetricsLogger { /** * Notifies the tracker that all windows of the app have been drawn. */ - void notifyWindowsDrawn(int windowingMode, long timestamp) { + WindowingModeTransitionInfoSnapshot notifyWindowsDrawn(int windowingMode, long timestamp) { if (DEBUG_METRICS) Slog.i(TAG, "notifyWindowsDrawn windowingMode=" + windowingMode); final WindowingModeTransitionInfo info = mWindowingModeTransitionInfo.get(windowingMode); if (info == null || info.loggedWindowsDrawn) { - return; + return null; } info.windowsDrawnDelayMs = calculateDelay(timestamp); info.loggedWindowsDrawn = true; + final WindowingModeTransitionInfoSnapshot infoSnapshot = + new WindowingModeTransitionInfoSnapshot(info); if (allWindowsDrawn() && mLoggedTransitionStarting) { - reset(false /* abort */); + reset(false /* abort */, info); } + return infoSnapshot; } /** @@ -394,7 +436,7 @@ class ActivityMetricsLogger { * Notifies the tracker that the app transition is starting. * * @param windowingModeToReason A map from windowing mode to a reason integer, which must be on - * of ActivityManagerInternal.APP_TRANSITION_* reasons. + * of ActivityTaskManagerInternal.APP_TRANSITION_* reasons. */ void notifyTransitionStarting(SparseIntArray windowingModeToReason, long timestamp) { if (!isAnyTransitionActive() || mLoggedTransitionStarting) { @@ -413,7 +455,7 @@ class ActivityMetricsLogger { info.reason = windowingModeToReason.valueAt(index); } if (allWindowsDrawn()) { - reset(false /* abort */); + reset(false /* abort */, null /* WindowingModeTransitionInfo */); } } @@ -452,8 +494,9 @@ class ActivityMetricsLogger { logAppTransitionCancel(info); mWindowingModeTransitionInfo.remove(r.getWindowingMode()); if (mWindowingModeTransitionInfo.size() == 0) { - reset(true /* abort */); + reset(true /* abort */, info); } + stopFullyDrawnTraceIfNeeded(); } } } @@ -488,19 +531,19 @@ class ActivityMetricsLogger { && mWindowingModeTransitionInfo.size() > 0; } - private void reset(boolean abort) { + private void reset(boolean abort, WindowingModeTransitionInfo info) { if (DEBUG_METRICS) Slog.i(TAG, "reset abort=" + abort); if (!abort && isAnyTransitionActive()) { logAppTransitionMultiEvents(); } + stopLaunchTrace(info); mCurrentTransitionStartTime = INVALID_START_TIME; - mCurrentTransitionDelayMs = -1; + mCurrentTransitionDelayMs = INVALID_DELAY; mLoggedTransitionStarting = false; mWindowingModeTransitionInfo.clear(); } private int calculateCurrentDelay() { - // Shouldn't take more than 25 days to launch an app, so int is fine here. return (int) (SystemClock.uptimeMillis() - mCurrentTransitionStartTime); } @@ -512,7 +555,7 @@ class ActivityMetricsLogger { private void logAppTransitionCancel(WindowingModeTransitionInfo info) { final int type = getTransitionType(info); - if (type == -1) { + if (type == INVALID_TRANSITION_TYPE) { return; } final LogMaker builder = new LogMaker(APP_TRANSITION_CANCELLED); @@ -533,7 +576,7 @@ class ActivityMetricsLogger { for (int index = mWindowingModeTransitionInfo.size() - 1; index >= 0; index--) { final WindowingModeTransitionInfo info = mWindowingModeTransitionInfo.valueAt(index); final int type = getTransitionType(info); - if (type == -1) { + if (type == INVALID_TRANSITION_TYPE) { return; } @@ -545,6 +588,7 @@ class ActivityMetricsLogger { final int currentTransitionDelayMs = mCurrentTransitionDelayMs; BackgroundThread.getHandler().post(() -> logAppTransition( currentTransitionDeviceUptime, currentTransitionDelayMs, infoSnapshot)); + BackgroundThread.getHandler().post(() -> logAppDisplayed(infoSnapshot)); info.launchedActivity.info.launchToken = null; } @@ -571,11 +615,11 @@ class ActivityMetricsLogger { currentTransitionDeviceUptime); builder.addTaggedData(APP_TRANSITION_DELAY_MS, currentTransitionDelayMs); builder.setSubtype(info.reason); - if (info.startingWindowDelayMs != -1) { + if (info.startingWindowDelayMs != INVALID_DELAY) { builder.addTaggedData(APP_TRANSITION_STARTING_WINDOW_DELAY_MS, info.startingWindowDelayMs); } - if (info.bindApplicationDelayMs != -1) { + if (info.bindApplicationDelayMs != INVALID_DELAY) { builder.addTaggedData(APP_TRANSITION_BIND_APPLICATION_DELAY_MS, info.bindApplicationDelayMs); } @@ -612,6 +656,24 @@ class ActivityMetricsLogger { logAppStartMemoryStateCapture(info); } + private void logAppDisplayed(WindowingModeTransitionInfoSnapshot info) { + if (info.type != TYPE_TRANSITION_WARM_LAUNCH && info.type != TYPE_TRANSITION_COLD_LAUNCH) { + return; + } + + EventLog.writeEvent(AM_ACTIVITY_LAUNCH_TIME, + info.userId, info.activityRecordIdHashCode, info.launchedActivityShortComponentName, + info.windowsDrawnDelayMs); + + StringBuilder sb = mStringBuilder; + sb.setLength(0); + sb.append("Displayed "); + sb.append(info.launchedActivityShortComponentName); + sb.append(": "); + TimeUtils.formatDuration(info.windowsDrawnDelayMs, sb); + Log.i(TAG, sb.toString()); + } + private int convertAppStartTransitionType(int tronType) { if (tronType == TYPE_TRANSITION_COLD_LAUNCH) { return StatsLog.APP_START_OCCURRED__TYPE__COLD; @@ -625,11 +687,12 @@ class ActivityMetricsLogger { return StatsLog.APP_START_OCCURRED__TYPE__UNKNOWN; } - void logAppTransitionReportedDrawn(ActivityRecord r, boolean restoredFromBundle) { + WindowingModeTransitionInfoSnapshot logAppTransitionReportedDrawn(ActivityRecord r, + boolean restoredFromBundle) { final WindowingModeTransitionInfo info = mLastWindowingModeTransitionInfo.get( r.getWindowingMode()); if (info == null) { - return; + return null; } final LogMaker builder = new LogMaker(APP_TRANSITION_REPORTED_DRAWN); builder.setPackageName(r.packageName); @@ -652,6 +715,25 @@ class ActivityMetricsLogger { info.launchedActivity.info.name, info.currentTransitionProcessRunning, startupTimeMs); + stopFullyDrawnTraceIfNeeded(); + final WindowingModeTransitionInfoSnapshot infoSnapshot = + new WindowingModeTransitionInfoSnapshot(info, r, (int) startupTimeMs); + BackgroundThread.getHandler().post(() -> logAppFullyDrawn(infoSnapshot)); + return infoSnapshot; + } + + private void logAppFullyDrawn(WindowingModeTransitionInfoSnapshot info) { + if (info.type != TYPE_TRANSITION_WARM_LAUNCH && info.type != TYPE_TRANSITION_COLD_LAUNCH) { + return; + } + + StringBuilder sb = mStringBuilder; + sb.setLength(0); + sb.append("Fully drawn "); + sb.append(info.launchedActivityShortComponentName); + sb.append(": "); + TimeUtils.formatDuration(info.windowsFullyDrawnDelayMs, sb); + Log.i(TAG, sb.toString()); } void logActivityStart(Intent intent, ProcessRecord callerApp, ActivityRecord r, @@ -753,7 +835,7 @@ class ActivityMetricsLogger { } else if (info.startResult == START_SUCCESS) { return TYPE_TRANSITION_COLD_LAUNCH; } - return -1; + return INVALID_TRANSITION_TYPE; } private void logAppStartMemoryStateCapture(WindowingModeTransitionInfoSnapshot info) { @@ -798,4 +880,46 @@ class ActivityMetricsLogger { } return mArtManagerInternal; } + + /** + * Starts traces for app launch and draw times. We stop the fully drawn trace if its already + * active since the app may not have reported fully drawn in the previous launch. + * + * See {@link android.app.Activity#reportFullyDrawn()} + * + * @param info + * */ + private void startTraces(WindowingModeTransitionInfo info) { + if (info == null) { + return; + } + stopFullyDrawnTraceIfNeeded(); + int transitionType = getTransitionType(info); + if (!info.launchTraceActive && transitionType == TYPE_TRANSITION_WARM_LAUNCH + || transitionType == TYPE_TRANSITION_COLD_LAUNCH) { + Trace.asyncTraceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "launching: " + + info.launchedActivity.packageName, 0); + Trace.asyncTraceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "drawing", 0); + mDrawingTraceActive = true; + info.launchTraceActive = true; + } + } + + private void stopLaunchTrace(WindowingModeTransitionInfo info) { + if (info == null) { + return; + } + if (info.launchTraceActive) { + Trace.asyncTraceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER, "launching: " + + info.launchedActivity.packageName, 0); + info.launchTraceActive = false; + } + } + + void stopFullyDrawnTraceIfNeeded() { + if (mDrawingTraceActive) { + Trace.asyncTraceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER, "drawing", 0); + mDrawingTraceActive = false; + } + } } diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java index 77cfb124ec80..5853ad976bbb 100644 --- a/services/core/java/com/android/server/am/ActivityRecord.java +++ b/services/core/java/com/android/server/am/ActivityRecord.java @@ -31,6 +31,7 @@ import static android.app.ActivityOptions.ANIM_THUMBNAIL_SCALE_UP; import static android.app.ActivityTaskManager.INVALID_STACK_ID; import static android.app.AppOpsManager.MODE_ALLOWED; import static android.app.AppOpsManager.OP_PICTURE_IN_PICTURE; +import static android.app.WaitResult.INVALID_DELAY; import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT; import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME; import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS; @@ -80,7 +81,6 @@ import static android.content.res.Configuration.UI_MODE_TYPE_VR_HEADSET; import static android.os.Build.VERSION_CODES.HONEYCOMB; import static android.os.Build.VERSION_CODES.O; import static android.os.Process.SYSTEM_UID; -import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER; import static android.view.WindowManagerPolicyConstants.NAV_BAR_LEFT; import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_CONFIGURATION; @@ -112,8 +112,6 @@ import static com.android.server.am.ActivityStack.LAUNCH_TICK; import static com.android.server.am.ActivityStack.LAUNCH_TICK_MSG; import static com.android.server.am.ActivityStack.PAUSE_TIMEOUT_MSG; import static com.android.server.am.ActivityStack.STOP_TIMEOUT_MSG; -import static com.android.server.am.EventLogTags.AM_ACTIVITY_FULLY_DRAWN_TIME; -import static com.android.server.am.EventLogTags.AM_ACTIVITY_LAUNCH_TIME; import static com.android.server.am.EventLogTags.AM_RELAUNCH_ACTIVITY; import static com.android.server.am.EventLogTags.AM_RELAUNCH_RESUME_ACTIVITY; import static com.android.server.am.TaskPersister.DEBUG; @@ -164,7 +162,6 @@ import android.os.PersistableBundle; import android.os.Process; import android.os.RemoteException; import android.os.SystemClock; -import android.os.Trace; import android.os.UserHandle; import android.os.storage.StorageManager; import android.service.voice.IVoiceInteractionSession; @@ -186,6 +183,7 @@ import com.android.internal.content.ReferrerIntent; import com.android.internal.util.XmlUtils; import com.android.server.AttributeCache; import com.android.server.AttributeCache.Entry; +import com.android.server.am.ActivityMetricsLogger.WindowingModeTransitionInfoSnapshot; import com.android.server.am.ActivityStack.ActivityState; import com.android.server.uri.UriPermissionOwner; import com.android.server.wm.AppWindowContainerController; @@ -266,9 +264,6 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo private int windowFlags; // custom window flags for preview window. private TaskRecord task; // the task this is in. private long createTime = System.currentTimeMillis(); - long displayStartTime; // when we started launching this activity - long fullyDrawnStartTime; // when we started launching this activity - private long startTime; // last time this activity was started long lastVisibleTime; // last time this activity became visible long cpuTimeAtResume; // the cpu time of host process at the time of resuming activity long pauseTime; // last time we started pausing the activity @@ -536,15 +531,6 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo pw.print("requestedVrComponent="); pw.println(requestedVrComponent); } - if (displayStartTime != 0 || startTime != 0) { - pw.print(prefix); pw.print("displayStartTime="); - if (displayStartTime == 0) pw.print("0"); - else TimeUtils.formatDuration(displayStartTime, now, pw); - pw.print(" startTime="); - if (startTime == 0) pw.print("0"); - else TimeUtils.formatDuration(startTime, now, pw); - pw.println(); - } final boolean waitingVisible = mStackSupervisor.mActivitiesWaitingForVisibleActivity.contains(this); if (lastVisibleTime != 0 || waitingVisible || nowVisible) { @@ -2006,79 +1992,13 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo } public void reportFullyDrawnLocked(boolean restoredFromBundle) { - final long curTime = SystemClock.uptimeMillis(); - if (displayStartTime != 0) { - reportLaunchTimeLocked(curTime); - } - final LaunchTimeTracker.Entry entry = mStackSupervisor.getLaunchTimeTracker().getEntry( - getWindowingMode()); - if (fullyDrawnStartTime != 0 && entry != null) { - final long thisTime = curTime - fullyDrawnStartTime; - final long totalTime = entry.mFullyDrawnStartTime != 0 - ? (curTime - entry.mFullyDrawnStartTime) : thisTime; - if (SHOW_ACTIVITY_START_TIME) { - Trace.asyncTraceEnd(TRACE_TAG_ACTIVITY_MANAGER, "drawing", 0); - EventLog.writeEvent(AM_ACTIVITY_FULLY_DRAWN_TIME, - userId, System.identityHashCode(this), shortComponentName, - thisTime, totalTime); - StringBuilder sb = service.mStringBuilder; - sb.setLength(0); - sb.append("Fully drawn "); - sb.append(shortComponentName); - sb.append(": "); - TimeUtils.formatDuration(thisTime, sb); - if (thisTime != totalTime) { - sb.append(" (total "); - TimeUtils.formatDuration(totalTime, sb); - sb.append(")"); - } - Log.i(TAG, sb.toString()); - } - if (totalTime > 0) { - //service.mUsageStatsService.noteFullyDrawnTime(realActivity, (int) totalTime); - } - entry.mFullyDrawnStartTime = 0; - } - mStackSupervisor.getActivityMetricsLogger().logAppTransitionReportedDrawn(this, - restoredFromBundle); - fullyDrawnStartTime = 0; - } - - private void reportLaunchTimeLocked(final long curTime) { - final LaunchTimeTracker.Entry entry = mStackSupervisor.getLaunchTimeTracker().getEntry( - getWindowingMode()); - if (entry == null) { - return; - } - final long thisTime = curTime - displayStartTime; - final long totalTime = entry.mLaunchStartTime != 0 - ? (curTime - entry.mLaunchStartTime) : thisTime; - if (SHOW_ACTIVITY_START_TIME) { - Trace.asyncTraceEnd(TRACE_TAG_ACTIVITY_MANAGER, "launching: " + packageName, 0); - EventLog.writeEvent(AM_ACTIVITY_LAUNCH_TIME, - userId, System.identityHashCode(this), shortComponentName, - thisTime, totalTime); - StringBuilder sb = service.mStringBuilder; - sb.setLength(0); - sb.append("Displayed "); - sb.append(shortComponentName); - sb.append(": "); - TimeUtils.formatDuration(thisTime, sb); - if (thisTime != totalTime) { - sb.append(" (total "); - TimeUtils.formatDuration(totalTime, sb); - sb.append(")"); - } - Log.i(TAG, sb.toString()); - } - mStackSupervisor.reportActivityLaunchedLocked(false, this, thisTime, totalTime); - if (totalTime > 0) { - //service.mUsageStatsService.noteLaunchTime(realActivity, (int)totalTime); + final WindowingModeTransitionInfoSnapshot info = mStackSupervisor + .getActivityMetricsLogger().logAppTransitionReportedDrawn(this, restoredFromBundle); + if (info != null) { + mStackSupervisor.reportActivityLaunchedLocked(false /* timeout */, this, + info.windowsFullyDrawnDelayMs); } - displayStartTime = 0; - entry.mLaunchStartTime = 0; } - @Override public void onStartingWindowDrawn(long timestamp) { synchronized (service.mGlobalLock) { @@ -2090,13 +2010,12 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo @Override public void onWindowsDrawn(long timestamp) { synchronized (service.mGlobalLock) { - mStackSupervisor.getActivityMetricsLogger().notifyWindowsDrawn(getWindowingMode(), - timestamp); - if (displayStartTime != 0) { - reportLaunchTimeLocked(timestamp); - } + final WindowingModeTransitionInfoSnapshot info = mStackSupervisor + .getActivityMetricsLogger().notifyWindowsDrawn(getWindowingMode(), timestamp); + final int windowsDrawnDelayMs = info != null ? info.windowsDrawnDelayMs : INVALID_DELAY; + mStackSupervisor.reportActivityLaunchedLocked(false /* timeout */, this, + windowsDrawnDelayMs); mStackSupervisor.sendWaitingVisibleReportLocked(this); - startTime = 0; finishLaunchTickingLocked(); if (task != null) { task.hasBeenVisible = true; diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java index 9f59bd8db62a..29b04ccdab08 100644 --- a/services/core/java/com/android/server/am/ActivityStack.java +++ b/services/core/java/com/android/server/am/ActivityStack.java @@ -151,7 +151,6 @@ import android.view.Display; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.app.IVoiceInteractor; -import com.android.internal.os.BatteryStatsImpl; import com.android.internal.util.function.pooled.PooledLambda; import com.android.server.Watchdog; import com.android.server.am.ActivityManagerService.ItemMatcher; @@ -1319,16 +1318,13 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai + " callers=" + Debug.getCallers(5)); r.setState(RESUMED, "minimalResumeActivityLocked"); r.completeResumeLocked(); - mStackSupervisor.getLaunchTimeTracker().setLaunchTime(r); if (DEBUG_SAVED_STATE) Slog.i(TAG_SAVED_STATE, "Launch completed; removing icicle of " + r.icicle); } private void clearLaunchTime(ActivityRecord r) { // Make sure that there is no activity waiting for this to launch. - if (mStackSupervisor.mWaitingActivityLaunched.isEmpty()) { - r.displayStartTime = r.fullyDrawnStartTime = 0; - } else { + if (!mStackSupervisor.mWaitingActivityLaunched.isEmpty()) { mStackSupervisor.removeTimeoutsForActivityLocked(r); mStackSupervisor.scheduleIdleTimeoutLocked(r); } @@ -1514,7 +1510,7 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai prev.getTask().touchActiveTime(); clearLaunchTime(prev); - mStackSupervisor.getLaunchTimeTracker().stopFullyDrawnTraceIfNeeded(getWindowingMode()); + mStackSupervisor.getActivityMetricsLogger().stopFullyDrawnTraceIfNeeded(); mService.updateCpuStats(); diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java index 877c8567b9d0..9688d263643c 100644 --- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java +++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java @@ -25,6 +25,7 @@ import static android.app.ActivityManager.START_TASK_TO_FRONT; import static android.app.ActivityTaskManager.INVALID_STACK_ID; import static android.app.ITaskStackListener.FORCED_RESIZEABLE_REASON_SECONDARY_DISPLAY; import static android.app.ITaskStackListener.FORCED_RESIZEABLE_REASON_SPLIT_SCREEN; +import static android.app.WaitResult.INVALID_DELAY; import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT; import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME; import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS; @@ -450,7 +451,6 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D private boolean mTaskLayersChanged = true; private ActivityMetricsLogger mActivityMetricsLogger; - private LaunchTimeTracker mLaunchTimeTracker = new LaunchTimeTracker(); private final ArrayList<ActivityRecord> mTmpActivityList = new ArrayList<>(); @@ -646,10 +646,6 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D return mActivityMetricsLogger; } - LaunchTimeTracker getLaunchTimeTracker() { - return mLaunchTimeTracker; - } - public KeyguardController getKeyguardController() { return mKeyguardController; } @@ -1179,8 +1175,8 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D } } - void waitActivityVisible(ComponentName name, WaitResult result) { - final WaitInfo waitInfo = new WaitInfo(name, result); + void waitActivityVisible(ComponentName name, WaitResult result, long startTimeMs) { + final WaitInfo waitInfo = new WaitInfo(name, result, startTimeMs); mWaitingForActivityVisible.add(waitInfo); } @@ -1211,8 +1207,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D changed = true; result.timeout = false; result.who = w.getComponent(); - result.totalTime = SystemClock.uptimeMillis() - result.thisTime; - result.thisTime = result.totalTime; + result.totalTime = SystemClock.uptimeMillis() - w.getStartTime(); mWaitingForActivityVisible.remove(w); } } @@ -1251,8 +1246,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D } } - void reportActivityLaunchedLocked(boolean timeout, ActivityRecord r, - long thisTime, long totalTime) { + void reportActivityLaunchedLocked(boolean timeout, ActivityRecord r, long totalTime) { boolean changed = false; for (int i = mWaitingActivityLaunched.size() - 1; i >= 0; i--) { WaitResult w = mWaitingActivityLaunched.remove(i); @@ -1262,7 +1256,6 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D if (r != null) { w.who = new ComponentName(r.info.packageName, r.info.name); } - w.thisTime = thisTime; w.totalTime = totalTime; // Do not modify w.result. } @@ -1728,8 +1721,6 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D ProcessRecord app = mService.mAm.getProcessRecordLocked(r.processName, r.info.applicationInfo.uid, true); - getLaunchTimeTracker().setLaunchTime(r); - if (app != null && app.thread != null) { try { if ((r.info.flags&ActivityInfo.FLAG_MULTIPROCESS) == 0 @@ -2082,7 +2073,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D mHandler.removeMessages(IDLE_TIMEOUT_MSG, r); r.finishLaunchTickingLocked(); if (fromTimeout) { - reportActivityLaunchedLocked(fromTimeout, r, -1, -1); + reportActivityLaunchedLocked(fromTimeout, r, INVALID_DELAY); } // This is a hack to semi-deal with a race condition @@ -4940,10 +4931,13 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D static class WaitInfo { private final ComponentName mTargetComponent; private final WaitResult mResult; + /** Time stamp when we started to wait for {@link WaitResult}. */ + private final long mStartTimeMs; - public WaitInfo(ComponentName targetComponent, WaitResult result) { + WaitInfo(ComponentName targetComponent, WaitResult result, long startTimeMs) { this.mTargetComponent = targetComponent; this.mResult = result; + this.mStartTimeMs = startTimeMs; } public boolean matches(ComponentName targetComponent) { @@ -4954,6 +4948,10 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D return mResult; } + public long getStartTime() { + return mStartTimeMs; + } + public ComponentName getComponent() { return mTargetComponent; } diff --git a/services/core/java/com/android/server/am/ActivityStarter.java b/services/core/java/com/android/server/am/ActivityStarter.java index 890aafefdf0f..8236bd0e763e 100644 --- a/services/core/java/com/android/server/am/ActivityStarter.java +++ b/services/core/java/com/android/server/am/ActivityStarter.java @@ -1154,6 +1154,9 @@ class ActivityStarter { mService.updateConfigurationLocked(globalConfig, null, false); } + // Notify ActivityMetricsLogger that the activity has launched. ActivityMetricsLogger + // will then wait for the windows to be drawn and populate WaitResult. + mSupervisor.getActivityMetricsLogger().notifyActivityLaunched(res, outRecord[0]); if (outResult != null) { outResult.result = res; @@ -1178,7 +1181,6 @@ class ActivityStarter { outResult.timeout = false; outResult.who = r.realActivity; outResult.totalTime = 0; - outResult.thisTime = 0; break; } case START_TASK_TO_FRONT: { @@ -1188,10 +1190,9 @@ class ActivityStarter { outResult.timeout = false; outResult.who = r.realActivity; outResult.totalTime = 0; - outResult.thisTime = 0; } else { - outResult.thisTime = SystemClock.uptimeMillis(); - mSupervisor.waitActivityVisible(r.realActivity, outResult); + final long startTimeMs = SystemClock.uptimeMillis(); + mSupervisor.waitActivityVisible(r.realActivity, outResult, startTimeMs); // Note: the timeout variable is not currently not ever set. do { try { @@ -1205,7 +1206,6 @@ class ActivityStarter { } } - mSupervisor.getActivityMetricsLogger().notifyActivityLaunched(res, outRecord[0]); return res; } } diff --git a/services/core/java/com/android/server/am/EventLogTags.logtags b/services/core/java/com/android/server/am/EventLogTags.logtags index ed891dfb0e70..0ef2a0a90e13 100644 --- a/services/core/java/com/android/server/am/EventLogTags.logtags +++ b/services/core/java/com/android/server/am/EventLogTags.logtags @@ -87,9 +87,6 @@ option java_package com.android.server.am # User switched 30041 am_switch_user (id|1|5) -# Activity fully drawn time -30042 am_activity_fully_drawn_time (User|1|5),(Token|1|5),(Component Name|3),(time|2|3) - # Activity set to resumed 30043 am_set_resumed_activity (User|1|5),(Component Name|3),(Reason|3) diff --git a/services/core/java/com/android/server/am/LaunchTimeTracker.java b/services/core/java/com/android/server/am/LaunchTimeTracker.java deleted file mode 100644 index ee869691f7ca..000000000000 --- a/services/core/java/com/android/server/am/LaunchTimeTracker.java +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License - */ - -package com.android.server.am; - -import android.app.WaitResult; -import android.os.SystemClock; -import android.os.Trace; -import android.util.SparseArray; - -/** - * Tracks launch time of apps to be reported by {@link WaitResult}. Note that this is slightly - * different from {@link ActivityMetricsLogger}, but should eventually merged with it. - */ -class LaunchTimeTracker { - - private final SparseArray<Entry> mWindowingModeLaunchTime = new SparseArray<>(); - - void setLaunchTime(ActivityRecord r) { - Entry entry = mWindowingModeLaunchTime.get(r.getWindowingMode()); - if (entry == null){ - entry = new Entry(); - mWindowingModeLaunchTime.append(r.getWindowingMode(), entry); - } - entry.setLaunchTime(r); - } - - void stopFullyDrawnTraceIfNeeded(int windowingMode) { - final Entry entry = mWindowingModeLaunchTime.get(windowingMode); - if (entry == null) { - return; - } - entry.stopFullyDrawnTraceIfNeeded(); - } - - Entry getEntry(int windowingMode) { - return mWindowingModeLaunchTime.get(windowingMode); - } - - static class Entry { - - long mLaunchStartTime; - long mFullyDrawnStartTime; - - void setLaunchTime(ActivityRecord r) { - if (r.displayStartTime == 0) { - r.fullyDrawnStartTime = r.displayStartTime = SystemClock.uptimeMillis(); - if (mLaunchStartTime == 0) { - startLaunchTraces(r.packageName); - mLaunchStartTime = mFullyDrawnStartTime = r.displayStartTime; - } - } else if (mLaunchStartTime == 0) { - startLaunchTraces(r.packageName); - mLaunchStartTime = mFullyDrawnStartTime = SystemClock.uptimeMillis(); - } - } - - private void startLaunchTraces(String packageName) { - if (mFullyDrawnStartTime != 0) { - Trace.asyncTraceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER, "drawing", 0); - } - Trace.asyncTraceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "launching: " + packageName, 0); - Trace.asyncTraceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "drawing", 0); - } - - private void stopFullyDrawnTraceIfNeeded() { - if (mFullyDrawnStartTime != 0 && mLaunchStartTime == 0) { - Trace.asyncTraceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER, "drawing", 0); - mFullyDrawnStartTime = 0; - } - } - } -} diff --git a/services/core/java/com/android/server/am/MemoryStatUtil.java b/services/core/java/com/android/server/am/MemoryStatUtil.java index 228c71d91b8f..a8e1ccca8b9d 100644 --- a/services/core/java/com/android/server/am/MemoryStatUtil.java +++ b/services/core/java/com/android/server/am/MemoryStatUtil.java @@ -38,6 +38,7 @@ import java.util.regex.Pattern; */ final class MemoryStatUtil { static final int BYTES_IN_KILOBYTE = 1024; + static final int PAGE_SIZE = 4096; private static final String TAG = TAG_WITH_CLASS_NAME ? "MemoryStatUtil" : TAG_AM; @@ -68,7 +69,7 @@ final class MemoryStatUtil { private static final int PGFAULT_INDEX = 9; private static final int PGMAJFAULT_INDEX = 11; - private static final int RSS_IN_BYTES_INDEX = 23; + private static final int RSS_IN_PAGES_INDEX = 23; private MemoryStatUtil() {} @@ -146,15 +147,15 @@ final class MemoryStatUtil { final MemoryStat memoryStat = new MemoryStat(); Matcher m; m = PGFAULT.matcher(memoryStatContents); - memoryStat.pgfault = m.find() ? Long.valueOf(m.group(1)) : 0; + memoryStat.pgfault = m.find() ? Long.parseLong(m.group(1)) : 0; m = PGMAJFAULT.matcher(memoryStatContents); - memoryStat.pgmajfault = m.find() ? Long.valueOf(m.group(1)) : 0; + memoryStat.pgmajfault = m.find() ? Long.parseLong(m.group(1)) : 0; m = RSS_IN_BYTES.matcher(memoryStatContents); - memoryStat.rssInBytes = m.find() ? Long.valueOf(m.group(1)) : 0; + memoryStat.rssInBytes = m.find() ? Long.parseLong(m.group(1)) : 0; m = CACHE_IN_BYTES.matcher(memoryStatContents); - memoryStat.cacheInBytes = m.find() ? Long.valueOf(m.group(1)) : 0; + memoryStat.cacheInBytes = m.find() ? Long.parseLong(m.group(1)) : 0; m = SWAP_IN_BYTES.matcher(memoryStatContents); - memoryStat.swapInBytes = m.find() ? Long.valueOf(m.group(1)) : 0; + memoryStat.swapInBytes = m.find() ? Long.parseLong(m.group(1)) : 0; return memoryStat; } @@ -163,7 +164,12 @@ final class MemoryStatUtil { if (memoryMaxUsageContents == null || memoryMaxUsageContents.isEmpty()) { return 0; } - return Long.valueOf(memoryMaxUsageContents); + try { + return Long.parseLong(memoryMaxUsageContents); + } catch (NumberFormatException e) { + Slog.e(TAG, "Failed to parse value", e); + return 0; + } } /** @@ -181,11 +187,16 @@ final class MemoryStatUtil { return null; } - final MemoryStat memoryStat = new MemoryStat(); - memoryStat.pgfault = Long.valueOf(splits[PGFAULT_INDEX]); - memoryStat.pgmajfault = Long.valueOf(splits[PGMAJFAULT_INDEX]); - memoryStat.rssInBytes = Long.valueOf(splits[RSS_IN_BYTES_INDEX]); - return memoryStat; + try { + final MemoryStat memoryStat = new MemoryStat(); + memoryStat.pgfault = Long.parseLong(splits[PGFAULT_INDEX]); + memoryStat.pgmajfault = Long.parseLong(splits[PGMAJFAULT_INDEX]); + memoryStat.rssInBytes = Long.parseLong(splits[RSS_IN_PAGES_INDEX]) * PAGE_SIZE; + return memoryStat; + } catch (NumberFormatException e) { + Slog.e(TAG, "Failed to parse value", e); + return null; + } } /** @@ -199,7 +210,7 @@ final class MemoryStatUtil { } Matcher m = RSS_HIGH_WATERMARK_IN_BYTES.matcher(procStatusContents); // Convert value read from /proc/pid/status from kilobytes to bytes. - return m.find() ? Long.valueOf(m.group(1)) * BYTES_IN_KILOBYTE : 0; + return m.find() ? Long.parseLong(m.group(1)) * BYTES_IN_KILOBYTE : 0; } /** diff --git a/services/core/java/com/android/server/am/PersistentConnection.java b/services/core/java/com/android/server/am/PersistentConnection.java index c5edb26892f8..3490b1d18953 100644 --- a/services/core/java/com/android/server/am/PersistentConnection.java +++ b/services/core/java/com/android/server/am/PersistentConnection.java @@ -24,7 +24,7 @@ import android.os.Handler; import android.os.IBinder; import android.os.SystemClock; import android.os.UserHandle; -import android.util.Slog; +import android.util.Log; import android.util.TimeUtils; import com.android.internal.annotations.GuardedBy; @@ -59,6 +59,8 @@ import java.io.PrintWriter; * know what to do when the service component has gone missing, for example. If the user of this * class wants to restore the connection, then it should call {@link #unbind()} and {@link #bind} * explicitly. + * + * atest ${ANDROID_BUILD_TOP}/frameworks/base/services/tests/mockingservicestests/src/com/android/server/am/PersistentConnectionTest.java */ public abstract class PersistentConnection<T> { private final Object mLock = new Object(); @@ -76,6 +78,7 @@ public abstract class PersistentConnection<T> { private final long mRebindBackoffMs; private final double mRebindBackoffIncrease; private final long mRebindMaxBackoffMs; + private final long mResetBackoffDelay; private long mReconnectTime; @@ -100,6 +103,18 @@ public abstract class PersistentConnection<T> { @GuardedBy("mLock") private T mService; + @GuardedBy("mLock") + private int mNumConnected; + + @GuardedBy("mLock") + private int mNumDisconnected; + + @GuardedBy("mLock") + private int mNumBindingDied; + + @GuardedBy("mLock") + private long mLastConnectedTime; + private final ServiceConnection mServiceConnection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { @@ -108,25 +123,35 @@ public abstract class PersistentConnection<T> { // Callback came in after PersistentConnection.unbind() was called. // We just ignore this. // (We've already called unbindService() already in unbind) - Slog.w(mTag, "Connected: " + mComponentName.flattenToShortString() + Log.w(mTag, "Connected: " + mComponentName.flattenToShortString() + " u" + mUserId + " but not bound, ignore."); return; } - Slog.i(mTag, "Connected: " + mComponentName.flattenToShortString() + Log.i(mTag, "Connected: " + mComponentName.flattenToShortString() + " u" + mUserId); + mNumConnected++; + mIsConnected = true; + mLastConnectedTime = injectUptimeMillis(); mService = asInterface(service); + + scheduleStableCheckLocked(); } } @Override public void onServiceDisconnected(ComponentName name) { synchronized (mLock) { - Slog.i(mTag, "Disconnected: " + mComponentName.flattenToShortString() + Log.i(mTag, "Disconnected: " + mComponentName.flattenToShortString() + " u" + mUserId); + mNumDisconnected++; + cleanUpConnectionLocked(); + + // Note we won't increase the rebind timeout here, because we don't explicitly + // rebind in this case. } } @@ -136,13 +161,16 @@ public abstract class PersistentConnection<T> { synchronized (mLock) { if (!mBound) { // Callback came in late? - Slog.w(mTag, "Binding died: " + mComponentName.flattenToShortString() + Log.w(mTag, "Binding died: " + mComponentName.flattenToShortString() + " u" + mUserId + " but not bound, ignore."); return; } - Slog.w(mTag, "Binding died: " + mComponentName.flattenToShortString() + Log.w(mTag, "Binding died: " + mComponentName.flattenToShortString() + " u" + mUserId); + + mNumBindingDied++; + scheduleRebindLocked(); } } @@ -152,7 +180,8 @@ public abstract class PersistentConnection<T> { public PersistentConnection(@NonNull String tag, @NonNull Context context, @NonNull Handler handler, int userId, @NonNull ComponentName componentName, - long rebindBackoffSeconds, double rebindBackoffIncrease, long rebindMaxBackoffSeconds) { + long rebindBackoffSeconds, double rebindBackoffIncrease, long rebindMaxBackoffSeconds, + long resetBackoffDelay) { mTag = tag; mContext = context; mHandler = handler; @@ -162,6 +191,7 @@ public abstract class PersistentConnection<T> { mRebindBackoffMs = rebindBackoffSeconds * 1000; mRebindBackoffIncrease = rebindBackoffIncrease; mRebindMaxBackoffMs = rebindMaxBackoffSeconds * 1000; + mResetBackoffDelay = resetBackoffDelay * 1000; mNextBackoffMs = mRebindBackoffMs; } @@ -170,6 +200,12 @@ public abstract class PersistentConnection<T> { return mComponentName; } + public final int getUserId() { + return mUserId; + } + + protected abstract int getBindFlags(); + /** * @return whether {@link #bind()} has been called and {@link #unbind()} hasn't. * @@ -220,6 +256,42 @@ public abstract class PersistentConnection<T> { } } + /** Return the next back-off time */ + public long getNextBackoffMs() { + synchronized (mLock) { + return mNextBackoffMs; + } + } + + /** Return the number of times the connected callback called. */ + public int getNumConnected() { + synchronized (mLock) { + return mNumConnected; + } + } + + /** Return the number of times the disconnected callback called. */ + public int getNumDisconnected() { + synchronized (mLock) { + return mNumDisconnected; + } + } + + /** Return the number of times the binding died callback called. */ + public int getNumBindingDied() { + synchronized (mLock) { + return mNumBindingDied; + } + } + + @GuardedBy("mLock") + private void resetBackoffLocked() { + if (mNextBackoffMs != mRebindBackoffMs) { + mNextBackoffMs = mRebindBackoffMs; + Log.i(mTag, "Backoff reset to " + mNextBackoffMs); + } + } + @GuardedBy("mLock") public final void bindInnerLocked(boolean resetBackoff) { unscheduleRebindLocked(); @@ -229,23 +301,24 @@ public abstract class PersistentConnection<T> { } mBound = true; + unscheduleStableCheckLocked(); + if (resetBackoff) { - // Note this is the only place we reset the backoff time. - mNextBackoffMs = mRebindBackoffMs; + resetBackoffLocked(); } final Intent service = new Intent().setComponent(mComponentName); if (DEBUG) { - Slog.d(mTag, "Attempting to connect to " + mComponentName); + Log.d(mTag, "Attempting to connect to " + mComponentName); } final boolean success = mContext.bindServiceAsUser(service, mServiceConnection, - Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE, + Context.BIND_AUTO_CREATE | getBindFlags(), mHandler, UserHandle.of(mUserId)); if (!success) { - Slog.e(mTag, "Binding: " + service.getComponent() + " u" + mUserId + Log.e(mTag, "Binding: " + service.getComponent() + " u" + mUserId + " failed."); } } @@ -265,6 +338,8 @@ public abstract class PersistentConnection<T> { private void cleanUpConnectionLocked() { mIsConnected = false; mService = null; + + unscheduleStableCheckLocked(); } /** @@ -275,6 +350,7 @@ public abstract class PersistentConnection<T> { mShouldBeBound = false; unbindLocked(); + unscheduleStableCheckLocked(); } } @@ -285,7 +361,7 @@ public abstract class PersistentConnection<T> { if (!mBound) { return; } - Slog.i(mTag, "Stopping: " + mComponentName.flattenToShortString() + " u" + mUserId); + Log.i(mTag, "Stopping: " + mComponentName.flattenToShortString() + " u" + mUserId); mBound = false; mContext.unbindService(mServiceConnection); @@ -303,7 +379,7 @@ public abstract class PersistentConnection<T> { unbindLocked(); if (!mRebindScheduled) { - Slog.i(mTag, "Scheduling to reconnect in " + mNextBackoffMs + " ms (uptime)"); + Log.i(mTag, "Scheduling to reconnect in " + mNextBackoffMs + " ms (uptime)"); mReconnectTime = injectUptimeMillis() + mNextBackoffMs; @@ -316,6 +392,33 @@ public abstract class PersistentConnection<T> { } } + private final Runnable mStableCheck = this::stableConnectionCheck; + + private void stableConnectionCheck() { + synchronized (mLock) { + final long now = injectUptimeMillis(); + final long timeRemaining = (mLastConnectedTime + mResetBackoffDelay) - now; + if (DEBUG) { + Log.d(mTag, "stableConnectionCheck: bound=" + mBound + " connected=" + mIsConnected + + " remaining=" + timeRemaining); + } + if (mBound && mIsConnected && timeRemaining <= 0) { + resetBackoffLocked(); + } + } + } + + @GuardedBy("mLock") + private void unscheduleStableCheckLocked() { + injectRemoveCallbacks(mStableCheck); + } + + @GuardedBy("mLock") + private void scheduleStableCheckLocked() { + unscheduleStableCheckLocked(); + injectPostAtTime(mStableCheck, injectUptimeMillis() + mResetBackoffDelay); + } + /** Must be implemented by a subclass to convert an {@link IBinder} to a stub. */ protected abstract T asInterface(IBinder binder); @@ -323,10 +426,12 @@ public abstract class PersistentConnection<T> { synchronized (mLock) { pw.print(prefix); pw.print(mComponentName.flattenToShortString()); - pw.print(mBound ? " [bound]" : " [not bound]"); - pw.print(mIsConnected ? " [connected]" : " [not connected]"); + pw.print(" u"); + pw.print(mUserId); + pw.print(mBound ? " [bound]" : " [not bound]"); + pw.print(mIsConnected ? " [connected]" : " [not connected]"); if (mRebindScheduled) { - pw.print(" reconnect in "); + pw.print(" reconnect in "); TimeUtils.formatDuration((mReconnectTime - injectUptimeMillis()), pw); } pw.println(); @@ -334,6 +439,20 @@ public abstract class PersistentConnection<T> { pw.print(prefix); pw.print(" Next backoff(sec): "); pw.print(mNextBackoffMs / 1000); + pw.println(); + + pw.print(prefix); + pw.print(" Connected: "); + pw.print(mNumConnected); + pw.print(" Disconnected: "); + pw.print(mNumDisconnected); + pw.print(" Died: "); + pw.print(mNumBindingDied); + if (mIsConnected) { + pw.print(" Duration: "); + TimeUtils.formatDuration((injectUptimeMillis() - mLastConnectedTime), pw); + } + pw.println(); } } @@ -373,6 +492,11 @@ public abstract class PersistentConnection<T> { } @VisibleForTesting + Runnable getStableCheckRunnableForTest() { + return mStableCheck; + } + + @VisibleForTesting boolean shouldBeBoundForTest() { return mShouldBeBound; } diff --git a/services/core/java/com/android/server/appbinding/AppBindingConstants.java b/services/core/java/com/android/server/appbinding/AppBindingConstants.java new file mode 100644 index 000000000000..b0088a824618 --- /dev/null +++ b/services/core/java/com/android/server/appbinding/AppBindingConstants.java @@ -0,0 +1,154 @@ +/* + * 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.appbinding; + +import android.content.Context; +import android.util.KeyValueListParser; +import android.util.Slog; + +import java.io.PrintWriter; +import java.util.concurrent.TimeUnit; + +/** + * Constants that are configurable via the global settings for {@link AppBindingService}. + */ +public class AppBindingConstants { + private static final String TAG = AppBindingService.TAG; + + private static final String SERVICE_RECONNECT_BACKOFF_SEC_KEY = + "service_reconnect_backoff_sec"; + + private static final String SERVICE_RECONNECT_BACKOFF_INCREASE_KEY = + "service_reconnect_backoff_increase"; + + private static final String SERVICE_RECONNECT_MAX_BACKOFF_SEC_KEY = + "service_reconnect_max_backoff_sec"; + + private static final String SERVICE_STABLE_CONNECTION_THRESHOLD_SEC_KEY = + "service_stable_connection_threshold_sec"; + + private static final String SMS_APP_BIND_FLAGS_KEY = + "sms_app_bind_flags"; + + public final String sourceSettings; + + /** + * The back-off before re-connecting, when a service binding died, due to the app + * crashing repeatedly. + */ + public final long SERVICE_RECONNECT_BACKOFF_SEC; + + /** + * The exponential back-off increase factor when a binding dies multiple times. + */ + public final double SERVICE_RECONNECT_BACKOFF_INCREASE; + + /** + * The max back-off + */ + public final long SERVICE_RECONNECT_MAX_BACKOFF_SEC; + + /** + * If a connection lasts more than this duration, we reset the re-connect back-off time. + */ + public final long SERVICE_STABLE_CONNECTION_THRESHOLD_SEC; + + /** + * Extra binding flags for SMS service. + */ + public final int SMS_APP_BIND_FLAGS; + + private AppBindingConstants(String settings) { + sourceSettings = settings; + + final KeyValueListParser parser = new KeyValueListParser(','); + try { + parser.setString(settings); + } catch (IllegalArgumentException e) { + // Failed to parse the settings string, log this and move on + // with defaults. + Slog.e(TAG, "Bad setting: " + settings); + } + + long serviceReconnectBackoffSec = parser.getLong( + SERVICE_RECONNECT_BACKOFF_SEC_KEY, 10); + + double serviceReconnectBackoffIncrease = parser.getFloat( + SERVICE_RECONNECT_BACKOFF_INCREASE_KEY, 2f); + + long serviceReconnectMaxBackoffSec = parser.getLong( + SERVICE_RECONNECT_MAX_BACKOFF_SEC_KEY, TimeUnit.HOURS.toSeconds(1)); + + int smsAppBindFlags = parser.getInt( + SMS_APP_BIND_FLAGS_KEY, + Context.BIND_NOT_VISIBLE | Context.BIND_FOREGROUND_SERVICE); + + long serviceStableConnectionThresholdSec = parser.getLong( + SERVICE_STABLE_CONNECTION_THRESHOLD_SEC_KEY, TimeUnit.MINUTES.toSeconds(2)); + + // Set minimum: 5 seconds. + serviceReconnectBackoffSec = Math.max(5, serviceReconnectBackoffSec); + + // Set minimum: 1.0. + serviceReconnectBackoffIncrease = + Math.max(1, serviceReconnectBackoffIncrease); + + // Make sure max >= default back off. + serviceReconnectMaxBackoffSec = Math.max(serviceReconnectBackoffSec, + serviceReconnectMaxBackoffSec); + + SERVICE_RECONNECT_BACKOFF_SEC = serviceReconnectBackoffSec; + SERVICE_RECONNECT_BACKOFF_INCREASE = serviceReconnectBackoffIncrease; + SERVICE_RECONNECT_MAX_BACKOFF_SEC = serviceReconnectMaxBackoffSec; + SERVICE_STABLE_CONNECTION_THRESHOLD_SEC = serviceStableConnectionThresholdSec; + SMS_APP_BIND_FLAGS = smsAppBindFlags; + } + + /** + * Create a new instance from a settings string. + */ + public static AppBindingConstants initializeFromString(String settings) { + return new AppBindingConstants(settings); + } + + /** + * dumpsys support. + */ + public void dump(String prefix, PrintWriter pw) { + pw.print(prefix); + pw.println("Constants:"); + + pw.print(prefix); + pw.print(" SERVICE_RECONNECT_BACKOFF_SEC: "); + pw.println(SERVICE_RECONNECT_BACKOFF_SEC); + + pw.print(prefix); + pw.print(" SERVICE_RECONNECT_BACKOFF_INCREASE: "); + pw.println(SERVICE_RECONNECT_BACKOFF_INCREASE); + + pw.print(prefix); + pw.print(" SERVICE_RECONNECT_MAX_BACKOFF_SEC: "); + pw.println(SERVICE_RECONNECT_MAX_BACKOFF_SEC); + + pw.print(prefix); + pw.print(" SERVICE_STABLE_CONNECTION_THRESHOLD_SEC: "); + pw.println(SERVICE_STABLE_CONNECTION_THRESHOLD_SEC); + + pw.print(prefix); + pw.print(" SMS_APP_BIND_FLAGS: 0x"); + pw.println(Integer.toHexString(SMS_APP_BIND_FLAGS)); + } +} diff --git a/services/core/java/com/android/server/appbinding/AppBindingService.java b/services/core/java/com/android/server/appbinding/AppBindingService.java index 91b3b21632c6..8c388092bb1b 100644 --- a/services/core/java/com/android/server/appbinding/AppBindingService.java +++ b/services/core/java/com/android/server/appbinding/AppBindingService.java @@ -16,26 +16,59 @@ package com.android.server.appbinding; +import android.annotation.NonNull; +import android.annotation.Nullable; import android.app.AppGlobals; +import android.content.BroadcastReceiver; +import android.content.ComponentName; +import android.content.ContentResolver; import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; import android.content.pm.IPackageManager; +import android.content.pm.ServiceInfo; +import android.database.ContentObserver; +import android.net.Uri; import android.os.Binder; import android.os.Handler; +import android.os.IBinder; +import android.os.IInterface; +import android.os.UserHandle; +import android.provider.Settings; +import android.provider.Settings.Global; +import android.text.TextUtils; +import android.util.Slog; +import android.util.SparseBooleanArray; +import com.android.internal.annotations.GuardedBy; +import com.android.internal.annotations.VisibleForTesting; import com.android.internal.os.BackgroundThread; import com.android.internal.util.DumpUtils; import com.android.server.SystemService; +import com.android.server.am.PersistentConnection; +import com.android.server.appbinding.finders.AppServiceFinder; +import com.android.server.appbinding.finders.SmsAppServiceFinder; import java.io.FileDescriptor; import java.io.PrintWriter; +import java.util.ArrayList; +import java.util.function.Consumer; /** * System server that keeps a binding to an app to keep it always running. + * + * <p>As of android Q, we only use it for the default SMS app. + * + * Relevant tests: + * atest CtsAppBindingHostTestCases + * + * TODO Maybe handle force-stop differently. Right now we just get "binder died" and re-bind + * after a timeout. b/116813347 */ public class AppBindingService extends Binder { public static final String TAG = "AppBindingService"; - private static final boolean DEBUG = false; + public static final boolean DEBUG = false; // DO NOT SUBMIT WITH TRUE private final Object mLock = new Object(); @@ -44,38 +77,492 @@ public class AppBindingService extends Binder { private final Handler mHandler; private final IPackageManager mIPackageManager; + @GuardedBy("mLock") + private AppBindingConstants mConstants; + + @GuardedBy("mLock") + private final SparseBooleanArray mRunningUsers = new SparseBooleanArray(2); + + @GuardedBy("mLock") + private final ArrayList<AppServiceFinder> mApps = new ArrayList<>(); + + @GuardedBy("mLock") + private final ArrayList<AppServiceConnection> mConnections = new ArrayList<>(); + static class Injector { public IPackageManager getIPackageManager() { return AppGlobals.getPackageManager(); } + + public String getGlobalSettingString(ContentResolver resolver, String key) { + return Settings.Global.getString(resolver, key); + } } /** - * System service interacts with this service via this class. + * {@link SystemService} for this service. */ - public static final class Lifecycle extends SystemService { + public static class Lifecycle extends SystemService { final AppBindingService mService; public Lifecycle(Context context) { + this(context, new Injector()); + } + + Lifecycle(Context context, Injector injector) { super(context); - mService = new AppBindingService(new Injector(), context); + mService = new AppBindingService(injector, context); } @Override public void onStart() { publishBinderService(Context.APP_BINDING_SERVICE, mService); } + + @Override + public void onBootPhase(int phase) { + mService.onBootPhase(phase); + } + + @Override + public void onStartUser(int userHandle) { + mService.onStartUser(userHandle); + } + + @Override + public void onUnlockUser(int userId) { + mService.onUnlockUser(userId); + } + + @Override + public void onStopUser(int userHandle) { + mService.onStopUser(userHandle); + } } private AppBindingService(Injector injector, Context context) { mInjector = injector; mContext = context; + mIPackageManager = injector.getIPackageManager(); + mHandler = BackgroundThread.getHandler(); + mApps.add(new SmsAppServiceFinder(context, this::onAppChanged, mHandler)); + + // Initialize with the default value to make it non-null. + mConstants = AppBindingConstants.initializeFromString(""); + } + + private void forAllAppsLocked(Consumer<AppServiceFinder> consumer) { + for (int i = 0; i < mApps.size(); i++) { + consumer.accept(mApps.get(i)); + } + } + + private void onBootPhase(int phase) { + if (DEBUG) { + Slog.d(TAG, "onBootPhase: " + phase); + } + switch (phase) { + case SystemService.PHASE_ACTIVITY_MANAGER_READY: + onPhaseActivityManagerReady(); + break; + case SystemService.PHASE_THIRD_PARTY_APPS_CAN_START: + onPhaseThirdPartyAppsCanStart(); + break; + } + } + + /** + * Handle boot phase PHASE_ACTIVITY_MANAGER_READY. + */ + private void onPhaseActivityManagerReady() { + final IntentFilter packageFilter = new IntentFilter(); + packageFilter.addAction(Intent.ACTION_PACKAGE_ADDED); + packageFilter.addAction(Intent.ACTION_PACKAGE_REMOVED); + packageFilter.addAction(Intent.ACTION_PACKAGE_CHANGED); + packageFilter.addDataScheme("package"); + + packageFilter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY); + mContext.registerReceiverAsUser(mPackageUserMonitor, UserHandle.ALL, + packageFilter, null, mHandler); + + final IntentFilter userFilter = new IntentFilter(); + userFilter.addAction(Intent.ACTION_USER_REMOVED); + mContext.registerReceiverAsUser(mPackageUserMonitor, UserHandle.ALL, + userFilter, null, mHandler); + + mContext.getContentResolver().registerContentObserver(Settings.Global.getUriFor( + Settings.Global.APP_BINDING_CONSTANTS), false, mSettingsObserver); + + refreshConstants(); + } + + private final ContentObserver mSettingsObserver = new ContentObserver(null) { + @Override + public void onChange(boolean selfChange) { + refreshConstants(); + } + }; + + private void refreshConstants() { + final String newSetting = mInjector.getGlobalSettingString( + mContext.getContentResolver(), Global.APP_BINDING_CONSTANTS); + + synchronized (mLock) { + if (TextUtils.equals(mConstants.sourceSettings, newSetting)) { + return; + } + Slog.i(TAG, "Updating constants with: " + newSetting); + mConstants = AppBindingConstants.initializeFromString(newSetting); + + rebindAllLocked("settings update"); + } + } + + @VisibleForTesting + final BroadcastReceiver mPackageUserMonitor = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + if (DEBUG) { + Slog.d(TAG, "Broadcast received: " + intent); + } + final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL); + if (userId == UserHandle.USER_NULL) { + Slog.w(TAG, "Intent broadcast does not contain user handle: " + intent); + return; + } + + final String action = intent.getAction(); + + if (Intent.ACTION_USER_REMOVED.equals(action)) { + onUserRemoved(userId); + return; + } + + final Uri intentUri = intent.getData(); + final String packageName = (intentUri != null) ? intentUri.getSchemeSpecificPart() + : null; + if (packageName == null) { + Slog.w(TAG, "Intent broadcast does not contain package name: " + intent); + return; + } + + final boolean replacing = intent.getBooleanExtra(Intent.EXTRA_REPLACING, false); + + switch (action) { + case Intent.ACTION_PACKAGE_ADDED: + if (replacing) { + handlePackageAddedReplacing(packageName, userId); + } + break; + case Intent.ACTION_PACKAGE_REMOVED: + if (!replacing) { + handlePackageRemoved(packageName, userId); + } + break; + case Intent.ACTION_PACKAGE_CHANGED: + handlePackageChanged(packageName, userId); + break; + } + } + }; + + /** + * Handle boot phase PHASE_THIRD_PARTY_APPS_CAN_START. + */ + private void onPhaseThirdPartyAppsCanStart() { + synchronized (mLock) { + forAllAppsLocked(AppServiceFinder::startMonitoring); + } + } + + /** User lifecycle callback. */ + private void onStartUser(int userId) { + if (DEBUG) { + Slog.d(TAG, "onStartUser: u" + userId); + } + synchronized (mLock) { + mRunningUsers.append(userId, true); + bindServicesLocked(userId, null, "user start"); + } + } + + /** User lifecycle callback. */ + private void onUnlockUser(int userId) { + if (DEBUG) { + Slog.d(TAG, "onUnlockUser: u" + userId); + } + synchronized (mLock) { + bindServicesLocked(userId, null, "user unlock"); + } + } + + /** User lifecycle callback. */ + private void onStopUser(int userId) { + if (DEBUG) { + Slog.d(TAG, "onStopUser: u" + userId); + } + synchronized (mLock) { + unbindServicesLocked(userId, null, "user stop"); + + mRunningUsers.delete(userId); + } + } + + private void onUserRemoved(int userId) { + if (DEBUG) { + Slog.d(TAG, "onUserRemoved: u" + userId); + } + synchronized (mLock) { + forAllAppsLocked((app) -> app.onUserRemoved(userId)); + + mRunningUsers.delete(userId); + } + } + + /** + * Called when a target package changes; e.g. when the user changes the default SMS app. + */ + private void onAppChanged(AppServiceFinder finder, int userId) { + if (DEBUG) { + Slog.d(TAG, "onAppChanged: u" + userId + " " + finder.getAppDescription()); + } + synchronized (mLock) { + final String reason = finder.getAppDescription() + " changed"; + unbindServicesLocked(userId, finder, reason); + bindServicesLocked(userId, finder, reason); + } + } + + @Nullable + private AppServiceFinder findFinderLocked(int userId, @NonNull String packageName) { + for (int i = 0; i < mApps.size(); i++) { + final AppServiceFinder app = mApps.get(i); + if (packageName.equals(app.getTargetPackage(userId))) { + return app; + } + } + return null; + } + + @Nullable + private AppServiceConnection findConnectionLock( + int userId, @NonNull AppServiceFinder target) { + for (int i = 0; i < mConnections.size(); i++) { + final AppServiceConnection conn = mConnections.get(i); + if ((conn.getUserId() == userId) && (conn.getFinder() == target)) { + return conn; + } + } + return null; + } + + private void handlePackageAddedReplacing(String packageName, int userId) { + if (DEBUG) { + Slog.d(TAG, "handlePackageAddedReplacing: u" + userId + " " + packageName); + } + synchronized (mLock) { + final AppServiceFinder finder = findFinderLocked(userId, packageName); + if (finder != null) { + unbindServicesLocked(userId, finder, "package update"); + bindServicesLocked(userId, finder, "package update"); + } + } + } + + private void handlePackageRemoved(String packageName, int userId) { + if (DEBUG) { + Slog.d(TAG, "handlePackageRemoved: u" + userId + " " + packageName); + } + synchronized (mLock) { + final AppServiceFinder finder = findFinderLocked(userId, packageName); + if (finder != null) { + unbindServicesLocked(userId, finder, "package uninstall"); + } + } + } + + private void handlePackageChanged(String packageName, int userId) { + if (DEBUG) { + Slog.d(TAG, "handlePackageChanged: u" + userId + " " + packageName); + } + synchronized (mLock) { + final AppServiceFinder finder = findFinderLocked(userId, packageName); + if (finder != null) { + unbindServicesLocked(userId, finder, "package changed"); + bindServicesLocked(userId, finder, "package changed"); + } + } + } + + private void rebindAllLocked(String reason) { + for (int i = 0; i < mRunningUsers.size(); i++) { + if (!mRunningUsers.valueAt(i)) { + continue; + } + final int userId = mRunningUsers.keyAt(i); + + unbindServicesLocked(userId, null, reason); + bindServicesLocked(userId, null, reason); + } + } + + private void bindServicesLocked(int userId, @Nullable AppServiceFinder target, + @NonNull String reasonForLog) { + for (int i = 0; i < mApps.size(); i++) { + final AppServiceFinder app = mApps.get(i); + if (target != null && target != app) { + continue; + } + + // Disconnect from existing binding. + final AppServiceConnection existingConn = findConnectionLock(userId, app); + if (existingConn != null) { + unbindServicesLocked(userId, target, reasonForLog); + } + + final ServiceInfo service = app.findService(userId, mIPackageManager); + if (service == null) { + continue; + } + if (DEBUG) { + Slog.d(TAG, "bindServicesLocked: u" + userId + " " + app.getAppDescription() + + " binding " + service.getComponentName() + " for " + reasonForLog); + } + final AppServiceConnection conn = + new AppServiceConnection(mContext, userId, mConstants, mHandler, + app, service.getComponentName()); + mConnections.add(conn); + conn.bind(); + } + } + + private void unbindServicesLocked(int userId, @Nullable AppServiceFinder target, + @NonNull String reasonForLog) { + for (int i = mConnections.size() - 1; i >= 0; i--) { + final AppServiceConnection conn = mConnections.get(i); + if ((conn.getUserId() != userId) + || (target != null && conn.getFinder() != target)) { + continue; + } + if (DEBUG) { + Slog.d(TAG, "unbindServicesLocked: u" + userId + + " " + conn.getFinder().getAppDescription() + + " unbinding " + conn.getComponentName() + " for " + reasonForLog); + } + mConnections.remove(i); + conn.unbind(); + } + } + + private static class AppServiceConnection extends PersistentConnection<IInterface> { + private final AppBindingConstants mConstants; + private final AppServiceFinder mFinder; + + AppServiceConnection(Context context, int userId, AppBindingConstants constants, + Handler handler, AppServiceFinder finder, + @NonNull ComponentName componentName) { + super(TAG, context, handler, userId, componentName, + constants.SERVICE_RECONNECT_BACKOFF_SEC, + constants.SERVICE_RECONNECT_BACKOFF_INCREASE, + constants.SERVICE_RECONNECT_MAX_BACKOFF_SEC, + constants.SERVICE_STABLE_CONNECTION_THRESHOLD_SEC); + mFinder = finder; + mConstants = constants; + } + + @Override + protected int getBindFlags() { + return mFinder.getBindFlags(mConstants); + } + + @Override + protected IInterface asInterface(IBinder obj) { + return mFinder.asInterface(obj); + } + + public AppServiceFinder getFinder() { + return mFinder; + } } @Override public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return; + + if (args.length > 0 && "-s".equals(args[0])) { + dumpSimple(pw); + return; + } + + synchronized (mLock) { + mConstants.dump(" ", pw); + + pw.println(); + pw.print(" Running users:"); + for (int i = 0; i < mRunningUsers.size(); i++) { + if (mRunningUsers.valueAt(i)) { + pw.print(" "); + pw.print(mRunningUsers.keyAt(i)); + } + } + + pw.println(); + pw.println(" Connections:"); + for (int i = 0; i < mConnections.size(); i++) { + final AppServiceConnection conn = mConnections.get(i); + pw.print(" App type: "); + pw.print(conn.getFinder().getAppDescription()); + pw.println(); + + conn.dump(" ", pw); + } + if (mConnections.size() == 0) { + pw.println(" None:"); + } + + pw.println(); + pw.println(" Finders:"); + forAllAppsLocked((app) -> app.dump(" ", pw)); + } + } + + /** + * Print simple output for CTS. + */ + private void dumpSimple(PrintWriter pw) { + synchronized (mLock) { + for (int i = 0; i < mConnections.size(); i++) { + final AppServiceConnection conn = mConnections.get(i); + + pw.print("conn,"); + pw.print(conn.getFinder().getAppDescription()); + pw.print(","); + pw.print(conn.getUserId()); + pw.print(","); + pw.print(conn.getComponentName().getPackageName()); + pw.print(","); + pw.print(conn.getComponentName().getClassName()); + pw.print(","); + pw.print(conn.isBound() ? "bound" : "not-bound"); + pw.print(","); + pw.print(conn.isConnected() ? "connected" : "not-connected"); + pw.print(",#con="); + pw.print(conn.getNumConnected()); + pw.print(",#dis="); + pw.print(conn.getNumDisconnected()); + pw.print(",#died="); + pw.print(conn.getNumBindingDied()); + pw.print(",backoff="); + pw.print(conn.getNextBackoffMs()); + pw.println(); + } + forAllAppsLocked((app) -> app.dumpSimple(pw)); + } + } + + AppBindingConstants getConstantsForTest() { + return mConstants; } } diff --git a/services/core/java/com/android/server/appbinding/AppBindingUtils.java b/services/core/java/com/android/server/appbinding/AppBindingUtils.java new file mode 100644 index 000000000000..fcbaecf7e059 --- /dev/null +++ b/services/core/java/com/android/server/appbinding/AppBindingUtils.java @@ -0,0 +1,89 @@ +/* + * 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.appbinding; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.content.Intent; +import android.content.pm.IPackageManager; +import android.content.pm.ParceledListSlice; +import android.content.pm.ResolveInfo; +import android.content.pm.ServiceInfo; +import android.os.RemoteException; +import android.util.Log; + +import java.util.List; + +/** + * Utility class to find a persistent bound service within an app. + */ +public class AppBindingUtils { + private static final String TAG = "AppBindingUtils"; + private AppBindingUtils() { + } + + /** + * Find a service with the action {@code serviceAction} in the package {@code packageName}. + * Returns null in any of the following cases. + * - No service with the action is found. + * - More than 1 service with the action is found. + * - Found service is not protected with the permission {@code servicePermission}. + */ + @Nullable + public static ServiceInfo findService(@NonNull String packageName, int userId, + String serviceAction, String servicePermission, + Class<?> serviceClassForLogging, + IPackageManager ipm, + StringBuilder errorMessage) { + final String simpleClassName = serviceClassForLogging.getSimpleName(); + final Intent intent = new Intent(serviceAction); + intent.setPackage(packageName); + + errorMessage.setLength(0); // Clear it. + try { + final ParceledListSlice<ResolveInfo> pls = ipm + .queryIntentServices(intent, null, /* flags=*/ 0, userId); + if (pls == null || pls.getList().size() == 0) { + errorMessage.append("Service with " + serviceAction + " not found."); + return null; + } + final List<ResolveInfo> list = pls.getList(); + // Note if multiple services are found, that's an error, even if only one of them + // is exported. + if (list.size() > 1) { + errorMessage.append("More than one " + simpleClassName + "'s found in package " + + packageName + ". They'll all be ignored."); + Log.e(TAG, errorMessage.toString()); + return null; + } + final ServiceInfo si = list.get(0).serviceInfo; + + if (!servicePermission.equals(si.permission)) { + errorMessage.append(simpleClassName + " " + + si.getComponentName().flattenToShortString() + + " must be protected with " + servicePermission + + "."); + Log.e(TAG, errorMessage.toString()); + return null; + } + return si; + } catch (RemoteException e) { + // Shouldn't happen + } + return null; + } +} diff --git a/services/core/java/com/android/server/appbinding/finders/AppServiceFinder.java b/services/core/java/com/android/server/appbinding/finders/AppServiceFinder.java new file mode 100644 index 000000000000..3d37317ad409 --- /dev/null +++ b/services/core/java/com/android/server/appbinding/finders/AppServiceFinder.java @@ -0,0 +1,233 @@ +/* + * 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.appbinding.finders; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.content.Context; +import android.content.pm.IPackageManager; +import android.content.pm.ServiceInfo; +import android.os.Handler; +import android.os.IBinder; +import android.os.IInterface; +import android.util.Log; +import android.util.Slog; +import android.util.SparseArray; + +import com.android.internal.annotations.GuardedBy; +import com.android.server.appbinding.AppBindingConstants; +import com.android.server.appbinding.AppBindingService; +import com.android.server.appbinding.AppBindingUtils; + +import java.io.PrintWriter; +import java.util.function.BiConsumer; + +/** + * Baseclss that finds "persistent" service from a type of an app. + * + * @param <TServiceType> Type of the target service class. + * @param <TServiceInterfaceType> Type of the IInterface class used by TServiceType. + */ +public abstract class AppServiceFinder<TServiceType, TServiceInterfaceType extends IInterface> { + protected static final String TAG = AppBindingService.TAG; + protected static final boolean DEBUG = AppBindingService.DEBUG; + + protected final Context mContext; + protected final BiConsumer<AppServiceFinder, Integer> mListener; + protected final Handler mHandler; + + private final Object mLock = new Object(); + + @GuardedBy("mLock") + private final SparseArray<String> mTargetPackages = new SparseArray(4); + + @GuardedBy("mLock") + private final SparseArray<ServiceInfo> mTargetServices = new SparseArray(4); + + @GuardedBy("mLock") + private final SparseArray<String> mLastMessages = new SparseArray(4); + + public AppServiceFinder(Context context, + BiConsumer<AppServiceFinder, Integer> listener, + Handler callbackHandler) { + mContext = context; + mListener = listener; + mHandler = callbackHandler; + } + + /** Human readable description of the type of apps; e.g. [Default SMS app] */ + @NonNull + public abstract String getAppDescription(); + + /** Start monitoring apps. (e.g. Start watching the default SMS app changes.) */ + public void startMonitoring() { + } + + /** Called when a user is removed. */ + public void onUserRemoved(int userId) { + synchronized (mLock) { + mTargetPackages.delete(userId); + mTargetServices.delete(userId); + mLastMessages.delete(userId); + } + } + + /** + * Find the target service from the target app on a given user. + */ + @Nullable + public final ServiceInfo findService(int userId, IPackageManager ipm) { + synchronized (mLock) { + mTargetPackages.put(userId, null); + mTargetServices.put(userId, null); + mLastMessages.put(userId, null); + + final String targetPackage = getTargetPackage(userId); + if (DEBUG) { + Slog.d(TAG, getAppDescription() + " package=" + targetPackage); + } + if (targetPackage == null) { + final String message = "Target package not found"; + mLastMessages.put(userId, message); + Slog.w(TAG, getAppDescription() + " u" + userId + " " + message); + return null; + } + mTargetPackages.put(userId, targetPackage); + + final StringBuilder errorMessage = new StringBuilder(); + final ServiceInfo service = AppBindingUtils.findService( + targetPackage, + userId, + getServiceAction(), + getServicePermission(), + getServiceClass(), + ipm, + errorMessage); + + if (service == null) { + final String message = errorMessage.toString(); + mLastMessages.put(userId, message); + if (DEBUG) { + // This log is optional because findService() already did Log.e(). + Slog.w(TAG, getAppDescription() + " package " + targetPackage + " u" + userId + + " " + message); + } + return null; + } + final String error = validateService(service); + if (error != null) { + mLastMessages.put(userId, error); + Log.e(TAG, error); + return null; + } + + final String message = "Valid service found"; + mLastMessages.put(userId, message); + mTargetServices.put(userId, service); + return service; + } + } + + protected abstract Class<TServiceType> getServiceClass(); + + /** + * Convert a binder reference to a service interface type. + */ + public abstract TServiceInterfaceType asInterface(IBinder obj); + + /** + * @return the target package on a given user. + */ + @Nullable + public abstract String getTargetPackage(int userId); + + /** + * @return the intent action that identifies the target service in the target app. + */ + @NonNull + protected abstract String getServiceAction(); + + /** + * @return the permission that the target service must be protected with. + */ + @NonNull + protected abstract String getServicePermission(); + + /** + * Subclass can implement it to decide whether to accept a service (by returning null) or not + * (by returning an error message.) + */ + protected String validateService(ServiceInfo service) { + return null; + } + + /** Return the bind flags for this service. */ + public abstract int getBindFlags(AppBindingConstants constants); + + /** Dumpsys support. */ + public void dump(String prefix, PrintWriter pw) { + pw.print(prefix); + pw.print("App type: "); + pw.print(getAppDescription()); + pw.println(); + + synchronized (mLock) { + for (int i = 0; i < mTargetPackages.size(); i++) { + final int userId = mTargetPackages.keyAt(i); + pw.print(prefix); + pw.print(" User: "); + pw.print(userId); + pw.println(); + + pw.print(prefix); + pw.print(" Package: "); + pw.print(mTargetPackages.get(userId)); + pw.println(); + + pw.print(prefix); + pw.print(" Service: "); + pw.print(mTargetServices.get(userId)); + pw.println(); + + pw.print(prefix); + pw.print(" Message: "); + pw.print(mLastMessages.get(userId)); + pw.println(); + } + } + } + + /** Dumpys support */ + public void dumpSimple(PrintWriter pw) { + synchronized (mLock) { + for (int i = 0; i < mTargetPackages.size(); i++) { + final int userId = mTargetPackages.keyAt(i); + pw.print("finder,"); + pw.print(getAppDescription()); + pw.print(","); + pw.print(userId); + pw.print(","); + pw.print(mTargetPackages.get(userId)); + pw.print(","); + pw.print(mTargetServices.get(userId)); + pw.print(","); + pw.print(mLastMessages.get(userId)); + pw.println(); + } + } + } +} diff --git a/services/core/java/com/android/server/appbinding/finders/SmsAppServiceFinder.java b/services/core/java/com/android/server/appbinding/finders/SmsAppServiceFinder.java new file mode 100644 index 000000000000..3340900ade7d --- /dev/null +++ b/services/core/java/com/android/server/appbinding/finders/SmsAppServiceFinder.java @@ -0,0 +1,114 @@ +/* + * 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.appbinding.finders; + +import static android.provider.Telephony.Sms.Intents.ACTION_DEFAULT_SMS_PACKAGE_CHANGED_INTERNAL; + +import android.Manifest.permission; +import android.app.ISmsAppService; +import android.app.SmsAppService; +import android.content.BroadcastReceiver; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.pm.ServiceInfo; +import android.os.Handler; +import android.os.IBinder; +import android.os.UserHandle; +import android.telephony.TelephonyManager; +import android.text.TextUtils; + +import com.android.internal.telephony.SmsApplication; +import com.android.server.appbinding.AppBindingConstants; + +import java.util.function.BiConsumer; + +/** + * Find the SmsAppService service within the default SMS app. + */ +public class SmsAppServiceFinder extends AppServiceFinder<SmsAppService, ISmsAppService> { + public SmsAppServiceFinder(Context context, + BiConsumer<AppServiceFinder, Integer> listener, + Handler callbackHandler) { + super(context, listener, callbackHandler); + } + + @Override + public String getAppDescription() { + return "[Default SMS app]"; + } + + @Override + protected Class<SmsAppService> getServiceClass() { + return SmsAppService.class; + } + + @Override + public ISmsAppService asInterface(IBinder obj) { + return ISmsAppService.Stub.asInterface(obj); + } + + @Override + protected String getServiceAction() { + return TelephonyManager.ACTION_SMS_APP_SERVICE; + } + + @Override + protected String getServicePermission() { + return permission.BIND_SMS_APP_SERVICE; + } + + @Override + public String getTargetPackage(int userId) { + final ComponentName cn = SmsApplication.getDefaultSmsApplicationAsUser( + mContext, /* updateIfNeeded= */ true, userId); + return cn == null ? null : cn.getPackageName(); + } + + @Override + public void startMonitoring() { + final IntentFilter filter = new IntentFilter(ACTION_DEFAULT_SMS_PACKAGE_CHANGED_INTERNAL); + mContext.registerReceiverAsUser(mSmsAppChangedWatcher, UserHandle.ALL, filter, + /* permission= */ null, mHandler); + } + + @Override + protected String validateService(ServiceInfo service) { + final String packageName = service.packageName; + final String process = service.processName; + + if (process == null || TextUtils.equals(packageName, process)) { + return "Service must not run on the main process"; + } + return null; // Null means accept this service. + } + + @Override + public int getBindFlags(AppBindingConstants constants) { + return constants.SMS_APP_BIND_FLAGS; + } + + private final BroadcastReceiver mSmsAppChangedWatcher = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + if (ACTION_DEFAULT_SMS_PACKAGE_CHANGED_INTERNAL.equals(intent.getAction())) { + mListener.accept(SmsAppServiceFinder.this, getSendingUserId()); + } + } + }; +} diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java index 66c7c437284b..f0ff570e385b 100644 --- a/services/core/java/com/android/server/audio/AudioService.java +++ b/services/core/java/com/android/server/audio/AudioService.java @@ -4685,7 +4685,9 @@ public class AudioService extends IAudioService.Stub @Override public void setHearingAidDeviceConnectionState(BluetoothDevice device, int state) { - Log.i(TAG, "setBluetoothHearingAidDeviceConnectionState"); + mDeviceLogger.log((new AudioEventLogger.StringEvent( + "setHearingAidDeviceConnectionState state=" + state + + " addr=" + device.getAddress())).printLog(TAG)); setBluetoothHearingAidDeviceConnectionState( device, state, false /* suppressNoisyIntent */, AudioSystem.DEVICE_NONE); @@ -4723,12 +4725,12 @@ public class AudioService extends IAudioService.Stub public int setBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent(BluetoothDevice device, int state, int profile, boolean suppressNoisyIntent, int a2dpVolume) { - mDeviceLogger.log(new AudioEventLogger.StringEvent( + mDeviceLogger.log((new AudioEventLogger.StringEvent( "setBluetoothA2dpDeviceConnectionStateSuppressNoisyIntent state=" + state // only querying address as this is the only readily available field on the device + " addr=" + device.getAddress() + " prof=" + profile + " supprNoisy=" + suppressNoisyIntent - + " vol=" + a2dpVolume)); + + " vol=" + a2dpVolume)).printLog(TAG)); if (mAudioHandler.hasMessages(MSG_SET_A2DP_SINK_CONNECTION_STATE, device)) { mDeviceLogger.log(new AudioEventLogger.StringEvent("A2DP connection state ignored")); return 0; @@ -5888,6 +5890,8 @@ public class AudioService extends IAudioService.Stub } private void onSendBecomingNoisyIntent() { + mDeviceLogger.log((new AudioEventLogger.StringEvent( + "broadcast ACTION_AUDIO_BECOMING_NOISY")).printLog(TAG)); sendBroadcastToAll(new Intent(AudioManager.ACTION_AUDIO_BECOMING_NOISY)); } @@ -7253,7 +7257,7 @@ public class AudioService extends IAudioService.Stub // - wired: logged before onSetWiredDeviceConnectionState() is executed // - A2DP: logged at reception of method call final private AudioEventLogger mDeviceLogger = new AudioEventLogger( - LOG_NB_EVENTS_DEVICE_CONNECTION, "wired/A2DP device connection"); + LOG_NB_EVENTS_DEVICE_CONNECTION, "wired/A2DP/hearing aid device connection"); final private AudioEventLogger mForceUseLogger = new AudioEventLogger( LOG_NB_EVENTS_FORCE_USE, diff --git a/services/core/java/com/android/server/clipboard/ClipboardService.java b/services/core/java/com/android/server/clipboard/ClipboardService.java index 873a8e314bb1..a769590447ab 100644 --- a/services/core/java/com/android/server/clipboard/ClipboardService.java +++ b/services/core/java/com/android/server/clipboard/ClipboardService.java @@ -629,6 +629,11 @@ public class ClipboardService extends SystemService { if (mAppOps.noteOp(op, callingUid, callingPackage) != AppOpsManager.MODE_ALLOWED) { return false; } + // Shell can access the clipboard for testing purposes. + if (mPm.checkPermission(android.Manifest.permission.READ_CLIPBOARD_IN_BACKGROUND, + callingPackage) == PackageManager.PERMISSION_GRANTED) { + return true; + } // The default IME is always allowed to access the clipboard. String defaultIme = Settings.Secure.getStringForUser(getContext().getContentResolver(), Settings.Secure.DEFAULT_INPUT_METHOD, UserHandle.getUserId(callingUid)); diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java index 2a80f0e7c291..48082b64ddfc 100644 --- a/services/core/java/com/android/server/connectivity/Vpn.java +++ b/services/core/java/com/android/server/connectivity/Vpn.java @@ -163,8 +163,8 @@ public class Vpn { // TODO: create separate trackers for each unique VPN to support // automated reconnection - private Context mContext; - private NetworkInfo mNetworkInfo; + private final Context mContext; + private final NetworkInfo mNetworkInfo; private String mPackage; private int mOwnerUID; private String mInterface; diff --git a/services/core/java/com/android/server/connectivity/tethering/TetheringConfiguration.java b/services/core/java/com/android/server/connectivity/tethering/TetheringConfiguration.java index 2b1d9196fe18..1e6bb04858a1 100644 --- a/services/core/java/com/android/server/connectivity/tethering/TetheringConfiguration.java +++ b/services/core/java/com/android/server/connectivity/tethering/TetheringConfiguration.java @@ -320,9 +320,8 @@ public class TetheringConfiguration { } private static boolean getEnableLegacyDhcpServer(Context ctx) { - // TODO: make the default false (0) and update javadoc in Settings.java final ContentResolver cr = ctx.getContentResolver(); - final int intVal = Settings.Global.getInt(cr, TETHER_ENABLE_LEGACY_DHCP_SERVER, 1); + final int intVal = Settings.Global.getInt(cr, TETHER_ENABLE_LEGACY_DHCP_SERVER, 0); return intVal != 0; } diff --git a/services/core/java/com/android/server/display/DisplayDevice.java b/services/core/java/com/android/server/display/DisplayDevice.java index 240592528565..7bfe9ce7017c 100644 --- a/services/core/java/com/android/server/display/DisplayDevice.java +++ b/services/core/java/com/android/server/display/DisplayDevice.java @@ -19,7 +19,6 @@ package com.android.server.display; import android.graphics.Rect; import android.hardware.display.DisplayViewport; import android.os.IBinder; -import android.view.Display; import android.view.Surface; import android.view.SurfaceControl; @@ -224,6 +223,8 @@ abstract class DisplayDevice { DisplayDeviceInfo info = getDisplayDeviceInfoLocked(); viewport.deviceWidth = isRotated ? info.height : info.width; viewport.deviceHeight = isRotated ? info.width : info.height; + + viewport.uniqueId = info.uniqueId; } /** diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java index 0eff7f57895b..e70460aef3e0 100644 --- a/services/core/java/com/android/server/display/DisplayManagerService.java +++ b/services/core/java/com/android/server/display/DisplayManagerService.java @@ -17,15 +17,14 @@ package com.android.server.display; import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR; +import static android.hardware.display.DisplayManager + .VIRTUAL_DISPLAY_FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD; import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY; import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC; import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_SECURE; -import static android.hardware.display.DisplayManager - .VIRTUAL_DISPLAY_FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD; - -import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.util.DumpUtils; -import com.android.internal.util.IndentingPrintWriter; +import static android.hardware.display.DisplayViewport.VIEWPORT_EXTERNAL; +import static android.hardware.display.DisplayViewport.VIEWPORT_INTERNAL; +import static android.hardware.display.DisplayViewport.VIEWPORT_VIRTUAL; import android.Manifest; import android.annotation.NonNull; @@ -45,8 +44,8 @@ import android.hardware.display.BrightnessConfiguration; import android.hardware.display.Curve; import android.hardware.display.DisplayManagerGlobal; import android.hardware.display.DisplayManagerInternal; -import android.hardware.display.DisplayViewport; import android.hardware.display.DisplayManagerInternal.DisplayTransactionListener; +import android.hardware.display.DisplayViewport; import android.hardware.display.IDisplayManager; import android.hardware.display.IDisplayManagerCallback; import android.hardware.display.IVirtualDisplayCallback; @@ -83,14 +82,17 @@ import android.view.DisplayInfo; import android.view.Surface; import android.view.SurfaceControl; -import com.android.internal.util.Preconditions; +import com.android.internal.annotations.GuardedBy; +import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.util.DumpUtils; +import com.android.internal.util.IndentingPrintWriter; import com.android.server.AnimationThread; import com.android.server.DisplayThread; import com.android.server.LocalServices; import com.android.server.SystemService; import com.android.server.UiThread; -import com.android.server.wm.WindowManagerInternal; import com.android.server.wm.SurfaceAnimationThread; +import com.android.server.wm.WindowManagerInternal; import java.io.FileDescriptor; import java.io.PrintWriter; @@ -256,9 +258,8 @@ public final class DisplayManagerService extends SystemService { // Viewports of the default display and the display that should receive touch // input from an external source. Used by the input system. - private final DisplayViewport mDefaultViewport = new DisplayViewport(); - private final DisplayViewport mExternalTouchViewport = new DisplayViewport(); - private final ArrayList<DisplayViewport> mVirtualTouchViewports = new ArrayList<>(); + @GuardedBy("mSyncRoot") + private final ArrayList<DisplayViewport> mViewports = new ArrayList<>(); // Persistent data store for all internal settings maintained by the display manager service. private final PersistentDataStore mPersistentDataStore = new PersistentDataStore(); @@ -272,9 +273,7 @@ public final class DisplayManagerService extends SystemService { // Temporary viewports, used when sending new viewport information to the // input system. May be used outside of the lock but only on the handler thread. - private final DisplayViewport mTempDefaultViewport = new DisplayViewport(); - private final DisplayViewport mTempExternalTouchViewport = new DisplayViewport(); - private final ArrayList<DisplayViewport> mTempVirtualTouchViewports = new ArrayList<>(); + private final ArrayList<DisplayViewport> mTempViewports = new ArrayList<>(); // The default color mode for default displays. Overrides the usual // Display.Display.COLOR_MODE_DEFAULT for displays with the @@ -1255,9 +1254,7 @@ public final class DisplayManagerService extends SystemService { } private void clearViewportsLocked() { - mDefaultViewport.valid = false; - mExternalTouchViewport.valid = false; - mVirtualTouchViewports.clear(); + mViewports.clear(); } private void configureDisplayLocked(SurfaceControl.Transaction t, DisplayDevice device) { @@ -1287,40 +1284,89 @@ public final class DisplayManagerService extends SystemService { } display.configureDisplayLocked(t, device, info.state == Display.STATE_OFF); - // Update the viewports if needed. - if (!mDefaultViewport.valid - && (info.flags & DisplayDeviceInfo.FLAG_DEFAULT_DISPLAY) != 0) { - setViewportLocked(mDefaultViewport, display, device); + // Update the corresponding viewport. + DisplayViewport internalViewport = getInternalViewportLocked(); + if ((info.flags & DisplayDeviceInfo.FLAG_DEFAULT_DISPLAY) != 0) { + populateViewportLocked(internalViewport, display, device); } - if (!mExternalTouchViewport.valid - && info.touch == DisplayDeviceInfo.TOUCH_EXTERNAL) { - setViewportLocked(mExternalTouchViewport, display, device); + DisplayViewport externalViewport = getExternalViewportLocked(); + if (info.touch == DisplayDeviceInfo.TOUCH_EXTERNAL) { + populateViewportLocked(externalViewport, display, device); + } else if (!externalViewport.valid) { + // TODO (b/116850516) move this logic into InputReader + externalViewport.copyFrom(internalViewport); + externalViewport.type = DisplayViewport.VIEWPORT_EXTERNAL; } if (info.touch == DisplayDeviceInfo.TOUCH_VIRTUAL && !TextUtils.isEmpty(info.uniqueId)) { - final DisplayViewport viewport = getVirtualTouchViewportLocked(info.uniqueId); - setViewportLocked(viewport, display, device); + final DisplayViewport viewport = getVirtualViewportLocked(info.uniqueId); + populateViewportLocked(viewport, display, device); } } - /** Gets the virtual device viewport or creates it if not yet created. */ - private DisplayViewport getVirtualTouchViewportLocked(@NonNull String uniqueId) { + /** Get the virtual device viewport that has the specified uniqueId. + * If such viewport does not exist, create it. */ + private DisplayViewport getVirtualViewportLocked(@NonNull String uniqueId) { DisplayViewport viewport; - final int count = mVirtualTouchViewports.size(); + final int count = mViewports.size(); for (int i = 0; i < count; i++) { - viewport = mVirtualTouchViewports.get(i); + viewport = mViewports.get(i); if (uniqueId.equals(viewport.uniqueId)) { + if (viewport.type != VIEWPORT_VIRTUAL) { + Slog.wtf(TAG, "Found a viewport with uniqueId '" + uniqueId + + "' but it has type " + DisplayViewport.typeToString(viewport.type) + + " (expected VIRTUAL)"); + continue; + } return viewport; } } viewport = new DisplayViewport(); viewport.uniqueId = uniqueId; - mVirtualTouchViewports.add(viewport); + viewport.type = VIEWPORT_VIRTUAL; + mViewports.add(viewport); return viewport; } - private static void setViewportLocked(DisplayViewport viewport, + private DisplayViewport getInternalViewportLocked() { + return getViewportByTypeLocked(VIEWPORT_INTERNAL); + } + + private DisplayViewport getExternalViewportLocked() { + return getViewportByTypeLocked(VIEWPORT_EXTERNAL); + } + + /** + * Get internal or external viewport. Create it if does not currently exist. + * @param viewportType - either INTERNAL or EXTERNAL + * @return the viewport with the requested type + */ + private DisplayViewport getViewportByTypeLocked(int viewportType) { + // Only allow a single INTERNAL or EXTERNAL viewport, which makes this function possible. + // TODO (b/116824030) allow multiple EXTERNAL viewports and remove this function. + // Creates the viewport if none exists. + if (viewportType != VIEWPORT_INTERNAL && viewportType != VIEWPORT_EXTERNAL) { + Slog.wtf(TAG, "Cannot call getViewportByTypeLocked for type " + + DisplayViewport.typeToString(viewportType)); + return null; + } + DisplayViewport viewport; + final int count = mViewports.size(); + for (int i = 0; i < count; i++) { + viewport = mViewports.get(i); + if (viewport.type == viewportType) { + return viewport; + } + } + + viewport = new DisplayViewport(); + viewport.type = viewportType; + mViewports.add(viewport); + return viewport; + } + + private static void populateViewportLocked(DisplayViewport viewport, LogicalDisplay display, DisplayDevice device) { viewport.valid = true; viewport.displayId = display.getDisplayIdLocked(); @@ -1400,9 +1446,7 @@ public final class DisplayManagerService extends SystemService { pw.println(" mPendingTraversal=" + mPendingTraversal); pw.println(" mGlobalDisplayState=" + Display.stateToString(mGlobalDisplayState)); pw.println(" mNextNonDefaultDisplayId=" + mNextNonDefaultDisplayId); - pw.println(" mDefaultViewport=" + mDefaultViewport); - pw.println(" mExternalTouchViewport=" + mExternalTouchViewport); - pw.println(" mVirtualTouchViewports=" + mVirtualTouchViewports); + pw.println(" mViewports=" + mViewports); pw.println(" mDefaultDisplayDefaultColorMode=" + mDefaultDisplayDefaultColorMode); pw.println(" mSingleDisplayDemoMode=" + mSingleDisplayDemoMode); pw.println(" mWifiDisplayScanRequestCount=" + mWifiDisplayScanRequestCount); @@ -1522,18 +1566,19 @@ public final class DisplayManagerService extends SystemService { break; case MSG_UPDATE_VIEWPORT: { + final boolean changed; synchronized (mSyncRoot) { - mTempDefaultViewport.copyFrom(mDefaultViewport); - mTempExternalTouchViewport.copyFrom(mExternalTouchViewport); - if (!mTempVirtualTouchViewports.equals(mVirtualTouchViewports)) { - mTempVirtualTouchViewports.clear(); - for (DisplayViewport d : mVirtualTouchViewports) { - mTempVirtualTouchViewports.add(d.makeCopy()); - } + changed = !mTempViewports.equals(mViewports); + if (changed) { + mTempViewports.clear(); + for (DisplayViewport d : mViewports) { + mTempViewports.add(d.makeCopy()); + } } } - mInputManagerInternal.setDisplayViewports(mTempDefaultViewport, - mTempExternalTouchViewport, mTempVirtualTouchViewports); + if (changed) { + mInputManagerInternal.setDisplayViewports(mTempViewports); + } break; } diff --git a/services/core/java/com/android/server/display/VirtualDisplayAdapter.java b/services/core/java/com/android/server/display/VirtualDisplayAdapter.java index 6111c23f252c..244c764ff215 100644 --- a/services/core/java/com/android/server/display/VirtualDisplayAdapter.java +++ b/services/core/java/com/android/server/display/VirtualDisplayAdapter.java @@ -19,13 +19,13 @@ package com.android.server.display; import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR; import static android.hardware.display.DisplayManager .VIRTUAL_DISPLAY_FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD; +import static android.hardware.display.DisplayManager + .VIRTUAL_DISPLAY_FLAG_DESTROY_CONTENT_ON_REMOVAL; import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_PRESENTATION; import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC; +import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_ROTATES_WITH_CONTENT; import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_SECURE; import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_SUPPORTS_TOUCH; -import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_ROTATES_WITH_CONTENT; -import static android.hardware.display.DisplayManager - .VIRTUAL_DISPLAY_FLAG_DESTROY_CONTENT_ON_REMOVAL; import android.content.Context; import android.hardware.display.IVirtualDisplayCallback; @@ -33,10 +33,10 @@ import android.media.projection.IMediaProjection; import android.media.projection.IMediaProjectionCallback; import android.os.Handler; import android.os.IBinder; -import android.os.SystemProperties; import android.os.IBinder.DeathRecipient; import android.os.Message; import android.os.RemoteException; +import android.os.SystemProperties; import android.util.ArrayMap; import android.util.Slog; import android.view.Display; @@ -60,7 +60,8 @@ public class VirtualDisplayAdapter extends DisplayAdapter { static final boolean DEBUG = false; // Unique id prefix for virtual displays - private static final String UNIQUE_ID_PREFIX = "virtual:"; + @VisibleForTesting + static final String UNIQUE_ID_PREFIX = "virtual:"; private final ArrayMap<IBinder, VirtualDisplayDevice> mVirtualDisplayDevices = new ArrayMap<IBinder, VirtualDisplayDevice>(); diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java index 0f2843917420..5bd095d952b0 100644 --- a/services/core/java/com/android/server/input/InputManagerService.java +++ b/services/core/java/com/android/server/input/InputManagerService.java @@ -16,9 +16,6 @@ package com.android.server.input; -import static android.hardware.display.DisplayViewport.VIEWPORT_INTERNAL; -import static android.hardware.display.DisplayViewport.VIEWPORT_EXTERNAL; - import android.annotation.NonNull; import android.app.IInputForwarder; import android.app.Notification; @@ -188,13 +185,8 @@ public class InputManagerService extends IInputManager.Stub private static native long nativeInit(InputManagerService service, Context context, MessageQueue messageQueue); private static native void nativeStart(long ptr); - private static native void nativeSetVirtualDisplayViewports(long ptr, + private static native void nativeSetDisplayViewports(long ptr, DisplayViewport[] viewports); - private static native void nativeSetDisplayViewport(long ptr, int viewportType, - int displayId, int rotation, - int logicalLeft, int logicalTop, int logicalRight, int logicalBottom, - int physicalLeft, int physicalTop, int physicalRight, int physicalBottom, - int deviceWidth, int deviceHeight, String uniqueId); private static native int nativeGetScanCodeState(long ptr, int deviceId, int sourceMask, int scanCode); @@ -409,31 +401,8 @@ public class InputManagerService extends IInputManager.Stub nativeReloadDeviceAliases(mPtr); } - private void setDisplayViewportsInternal(DisplayViewport defaultViewport, - DisplayViewport externalTouchViewport, - List<DisplayViewport> virtualTouchViewports) { - if (defaultViewport.valid) { - setDisplayViewport(VIEWPORT_INTERNAL, defaultViewport); - } - - if (externalTouchViewport.valid) { - setDisplayViewport(VIEWPORT_EXTERNAL, externalTouchViewport); - } else if (defaultViewport.valid) { - setDisplayViewport(VIEWPORT_EXTERNAL, defaultViewport); - } - - nativeSetVirtualDisplayViewports(mPtr, - virtualTouchViewports.toArray(new DisplayViewport[0])); - } - - private void setDisplayViewport(int viewportType, DisplayViewport viewport) { - nativeSetDisplayViewport(mPtr, viewportType, - viewport.displayId, viewport.orientation, - viewport.logicalFrame.left, viewport.logicalFrame.top, - viewport.logicalFrame.right, viewport.logicalFrame.bottom, - viewport.physicalFrame.left, viewport.physicalFrame.top, - viewport.physicalFrame.right, viewport.physicalFrame.bottom, - viewport.deviceWidth, viewport.deviceHeight, viewport.uniqueId); + private void setDisplayViewportsInternal(List<DisplayViewport> viewports) { + nativeSetDisplayViewports(mPtr, viewports.toArray(new DisplayViewport[0])); } /** @@ -2203,11 +2172,8 @@ public class InputManagerService extends IInputManager.Stub private final class LocalService extends InputManagerInternal { @Override - public void setDisplayViewports(DisplayViewport defaultViewport, - DisplayViewport externalTouchViewport, - List<DisplayViewport> virtualTouchViewports) { - setDisplayViewportsInternal(defaultViewport, externalTouchViewport, - virtualTouchViewports); + public void setDisplayViewports(List<DisplayViewport> viewports) { + setDisplayViewportsInternal(viewports); } @Override diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index cade07cfb821..5005ea7e55f8 100644 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -4289,7 +4289,8 @@ public class NotificationManagerService extends SystemService { } // posted from app A on behalf of app A if (isCallerSameApp(targetPkg, callingUid, userId) - && TextUtils.equals(callingPkg, targetPkg)) { + && (TextUtils.equals(callingPkg, targetPkg) + || isCallerSameApp(callingPkg, callingUid, userId))) { return callingUid; } @@ -4306,7 +4307,8 @@ public class NotificationManagerService extends SystemService { return targetUid; } - throw new SecurityException("Caller " + callingUid + " cannot post for pkg " + targetPkg); + throw new SecurityException("Caller " + callingPkg + ":" + callingUid + + " cannot post for pkg " + targetPkg + " in user " + userId); } /** @@ -4326,7 +4328,7 @@ public class NotificationManagerService extends SystemService { if (!isSystemNotification && !isNotificationFromListener) { synchronized (mNotificationLock) { if (mNotificationsByKey.get(r.sbn.getKey()) == null - && isCallerInstantApp(pkg, callingUid, r.getUserId())) { + && isCallerInstantApp(pkg, Binder.getCallingUid(), userId)) { // Ephemeral apps have some special constraints for notifications. // They are not allowed to create new notifications however they are allowed to // update notifications created by the system (e.g. a foreground service @@ -4732,7 +4734,8 @@ public class NotificationManagerService extends SystemService { if (notification.getSmallIcon() != null) { StatusBarNotification oldSbn = (old != null) ? old.sbn : null; mListeners.notifyPostedLocked(r, old); - if (oldSbn == null || !Objects.equals(oldSbn.getGroup(), n.getGroup())) { + if ((oldSbn == null || !Objects.equals(oldSbn.getGroup(), n.getGroup())) + && !isCritical(r)) { mHandler.post(new Runnable() { @Override public void run() { @@ -4902,6 +4905,19 @@ public class NotificationManagerService extends SystemService { } /** + * Check if the notification is classified as critical. + * + * @param record the record to test for criticality + * @return {@code true} if notification is considered critical + * + * @see CriticalNotificationExtractor for criteria + */ + private boolean isCritical(NotificationRecord record) { + // 0 is the most critical + return record.getCriticality() < CriticalNotificationExtractor.NORMAL; + } + + /** * Keeps the last 5 packages that have notified, by user. */ @GuardedBy("mNotificationLock") diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java index 8f2833f68192..006ea75ea160 100644 --- a/services/core/java/com/android/server/pm/PackageInstallerSession.java +++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java @@ -21,6 +21,7 @@ import static android.content.pm.PackageManager.INSTALL_FAILED_CONTAINER_ERROR; import static android.content.pm.PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE; import static android.content.pm.PackageManager.INSTALL_FAILED_INTERNAL_ERROR; import static android.content.pm.PackageManager.INSTALL_FAILED_INVALID_APK; +import static android.content.pm.PackageManager.INSTALL_FAILED_MISSING_SPLIT; import static android.content.pm.PackageParser.APK_FILE_EXTENSION; import static android.system.OsConstants.O_CREAT; import static android.system.OsConstants.O_RDONLY; @@ -1060,6 +1061,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { @GuardedBy("mLock") private void validateInstallLocked(@Nullable PackageInfo pkgInfo) throws PackageManagerException { + ApkLite baseApk = null; mPackageName = null; mVersionCode = -1; mSigningDetails = PackageParser.SigningDetails.UNKNOWN; @@ -1136,6 +1138,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { // Base is coming from session if (apk.splitName == null) { mResolvedBaseFile = targetFile; + baseApk = apk; } mResolvedStagedFiles.add(targetFile); @@ -1221,6 +1224,7 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { if (baseDexMetadataFile != null) { mResolvedInheritedFiles.add(baseDexMetadataFile); } + baseApk = existingBase; } // Inherit splits if not overridden @@ -1300,6 +1304,10 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { } } } + if (baseApk.isSplitRequired && stagedSplits.size() <= 1) { + throw new PackageManagerException(INSTALL_FAILED_MISSING_SPLIT, + "Missing split for " + mPackageName); + } } @GuardedBy("mLock") diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 10980b79f1f4..329b1da82608 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -8474,7 +8474,7 @@ public class PackageManagerService extends IPackageManager.Stub private boolean canSkipFullApkVerification(String apkPath) { final byte[] rootHashObserved; try { - rootHashObserved = VerityUtils.generateFsverityRootHash(apkPath); + rootHashObserved = VerityUtils.generateApkVerityRootHash(apkPath); if (rootHashObserved == null) { return false; // APK does not contain Merkle tree root hash. } @@ -16010,7 +16010,8 @@ public class PackageManagerService extends IPackageManager.Stub if (Build.IS_DEBUGGABLE) Slog.i(TAG, "Enabling apk verity to " + apkPath); FileDescriptor fd = result.getUnownedFileDescriptor(); try { - final byte[] signedRootHash = VerityUtils.generateFsverityRootHash(apkPath); + final byte[] signedRootHash = + VerityUtils.generateApkVerityRootHash(apkPath); mInstaller.installApkVerity(apkPath, fd, result.getContentSize()); mInstaller.assertFsverityRootHashMatches(apkPath, signedRootHash); } catch (InstallerException | IOException | DigestException | @@ -23098,7 +23099,9 @@ public class PackageManagerService extends IPackageManager.Stub return false; } } - if (sUserManager.hasUserRestriction(UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES, userId)) { + if (sUserManager.hasUserRestriction(UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES, userId) + || sUserManager.hasUserRestriction( + UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES_GLOBALLY, userId)) { return false; } if (mExternalSourcesPolicy != null) { diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java index a9f1b5c05a7f..93729d1949b0 100644 --- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java +++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java @@ -124,6 +124,7 @@ class PackageManagerShellCommand extends ShellCommand { int mTargetUser; boolean mBrief; boolean mComponents; + int mQueryFlags; PackageManagerShellCommand(PackageManagerService service) { mInterface = service; @@ -739,6 +740,9 @@ class PackageManagerShellCommand extends ShellCommand { } else if ("--components".equals(opt)) { mComponents = true; return true; + } else if ("--query-flags".equals(opt)) { + mQueryFlags = Integer.decode(cmd.getNextArgRequired()); + return true; } return false; } @@ -784,7 +788,8 @@ class PackageManagerShellCommand extends ShellCommand { throw new RuntimeException(e.getMessage(), e); } try { - ResolveInfo ri = mInterface.resolveIntent(intent, intent.getType(), 0, mTargetUser); + ResolveInfo ri = mInterface.resolveIntent(intent, intent.getType(), mQueryFlags, + mTargetUser); PrintWriter pw = getOutPrintWriter(); if (ri == null) { pw.println("No activity found"); @@ -806,8 +811,8 @@ class PackageManagerShellCommand extends ShellCommand { throw new RuntimeException(e.getMessage(), e); } try { - List<ResolveInfo> result = mInterface.queryIntentActivities(intent, intent.getType(), 0, - mTargetUser).getList(); + List<ResolveInfo> result = mInterface.queryIntentActivities(intent, intent.getType(), + mQueryFlags, mTargetUser).getList(); PrintWriter pw = getOutPrintWriter(); if (result == null || result.size() <= 0) { pw.println("No activities found"); @@ -840,8 +845,8 @@ class PackageManagerShellCommand extends ShellCommand { throw new RuntimeException(e.getMessage(), e); } try { - List<ResolveInfo> result = mInterface.queryIntentServices(intent, intent.getType(), 0, - mTargetUser).getList(); + List<ResolveInfo> result = mInterface.queryIntentServices(intent, intent.getType(), + mQueryFlags, mTargetUser).getList(); PrintWriter pw = getOutPrintWriter(); if (result == null || result.size() <= 0) { pw.println("No services found"); @@ -874,8 +879,8 @@ class PackageManagerShellCommand extends ShellCommand { throw new RuntimeException(e.getMessage(), e); } try { - List<ResolveInfo> result = mInterface.queryIntentReceivers(intent, intent.getType(), 0, - mTargetUser).getList(); + List<ResolveInfo> result = mInterface.queryIntentReceivers(intent, intent.getType(), + mQueryFlags, mTargetUser).getList(); PrintWriter pw = getOutPrintWriter(); if (result == null || result.size() <= 0) { pw.println("No receivers found"); @@ -2731,16 +2736,20 @@ class PackageManagerShellCommand extends ShellCommand { pw.println(" -d: only list dangerous permissions"); pw.println(" -u: list only the permissions users will see"); pw.println(""); - pw.println(" resolve-activity [--brief] [--components] [--user USER_ID] INTENT"); + pw.println(" resolve-activity [--brief] [--components] [--query-flags FLAGS]"); + pw.println(" [--user USER_ID] INTENT"); pw.println(" Prints the activity that resolves to the given INTENT."); pw.println(""); - pw.println(" query-activities [--brief] [--components] [--user USER_ID] INTENT"); + pw.println(" query-activities [--brief] [--components] [--query-flags FLAGS]"); + pw.println(" [--user USER_ID] INTENT"); pw.println(" Prints all activities that can handle the given INTENT."); pw.println(""); - pw.println(" query-services [--brief] [--components] [--user USER_ID] INTENT"); + pw.println(" query-services [--brief] [--components] [--query-flags FLAGS]"); + pw.println(" [--user USER_ID] INTENT"); pw.println(" Prints all services that can handle the given INTENT."); pw.println(""); - pw.println(" query-receivers [--brief] [--components] [--user USER_ID] INTENT"); + pw.println(" query-receivers [--brief] [--components] [--query-flags FLAGS]"); + pw.println(" [--user USER_ID] INTENT"); pw.println(" Prints all broadcast receivers that can handle the given INTENT."); pw.println(""); pw.println(" install [-lrtsfdg] [-i PACKAGE] [--user USER_ID|all|current]"); diff --git a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java index 3f28ee659a58..13155027a387 100644 --- a/services/core/java/com/android/server/pm/UserRestrictionsUtils.java +++ b/services/core/java/com/android/server/pm/UserRestrictionsUtils.java @@ -16,10 +16,6 @@ package com.android.server.pm; -import com.google.android.collect.Sets; - -import com.android.internal.util.Preconditions; - import android.annotation.NonNull; import android.annotation.Nullable; import android.app.ActivityManager; @@ -42,6 +38,10 @@ import android.util.Log; import android.util.Slog; import android.util.SparseArray; +import com.android.internal.util.Preconditions; + +import com.google.android.collect.Sets; + import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlSerializer; @@ -77,6 +77,7 @@ public class UserRestrictionsUtils { UserManager.DISALLOW_UNINSTALL_APPS, UserManager.DISALLOW_SHARE_LOCATION, UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES, + UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES_GLOBALLY, UserManager.DISALLOW_CONFIG_BLUETOOTH, UserManager.DISALLOW_BLUETOOTH, UserManager.DISALLOW_BLUETOOTH_SHARING, @@ -211,7 +212,8 @@ public class UserRestrictionsUtils { */ private static final Set<String> PROFILE_GLOBAL_RESTRICTIONS = Sets.newArraySet( UserManager.ENSURE_VERIFY_APPS, - UserManager.DISALLOW_AIRPLANE_MODE + UserManager.DISALLOW_AIRPLANE_MODE, + UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES_GLOBALLY ); /** @@ -517,13 +519,18 @@ public class UserRestrictionsUtils { userId); } break; + case UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES_GLOBALLY: + setInstallMarketAppsRestriction(cr, userId, getNewUserRestrictionSetting( + context, userId, UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES, + newValue)); + break; case UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES: // Since Android O, the secure setting is not available to be changed by the // user. Hence, when the restriction is cleared, we need to reset the state of // the setting to its default value which is now 1. - android.provider.Settings.Secure.putIntForUser(cr, - android.provider.Settings.Secure.INSTALL_NON_MARKET_APPS, - newValue ? 0 : 1, userId); + setInstallMarketAppsRestriction(cr, userId, getNewUserRestrictionSetting( + context, userId, UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES_GLOBALLY, + newValue)); break; case UserManager.DISALLOW_RUN_IN_BACKGROUND: if (newValue) { @@ -813,4 +820,16 @@ public class UserRestrictionsUtils { } return false; } + + private static void setInstallMarketAppsRestriction(ContentResolver cr, int userId, + int settingValue) { + android.provider.Settings.Secure.putIntForUser( + cr, android.provider.Settings.Secure.INSTALL_NON_MARKET_APPS, settingValue, userId); + } + + private static int getNewUserRestrictionSetting(Context context, int userId, + String userRestriction, boolean newValue) { + return (newValue || UserManager.get(context).hasUserRestriction(userRestriction, + UserHandle.of(userId))) ? 0 : 1; + } } diff --git a/services/core/java/com/android/server/pm/dex/TEST_MAPPING b/services/core/java/com/android/server/pm/dex/TEST_MAPPING new file mode 100644 index 000000000000..ad5255904d20 --- /dev/null +++ b/services/core/java/com/android/server/pm/dex/TEST_MAPPING @@ -0,0 +1,22 @@ +{ + "presubmit": [ + { + "name": "DexLoggerTests" + }, + { + "name": "DexManagerTests" + }, + { + "name": "DexoptOptionsTests" + }, + { + "name": "DexoptUtilsTest" + }, + { + "name": "PackageDexUsageTests" + }, + { + "name": "DexLoggerIntegrationTests" + } + ] +} diff --git a/services/core/java/com/android/server/pm/permission/PermissionsState.java b/services/core/java/com/android/server/pm/permission/PermissionsState.java index 5e66bfc3cd3e..82d6b226df9f 100644 --- a/services/core/java/com/android/server/pm/permission/PermissionsState.java +++ b/services/core/java/com/android/server/pm/permission/PermissionsState.java @@ -135,7 +135,8 @@ public final class PermissionsState { final int userCount = other.mPermissionReviewRequired.size(); for (int i = 0; i < userCount; i++) { final boolean reviewRequired = other.mPermissionReviewRequired.valueAt(i); - mPermissionReviewRequired.put(i, reviewRequired); + mPermissionReviewRequired.put(other.mPermissionReviewRequired.keyAt(i), + reviewRequired); } } } diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java index 9f6b3dde5a49..b3f2a27cf99a 100644 --- a/services/core/java/com/android/server/power/PowerManagerService.java +++ b/services/core/java/com/android/server/power/PowerManagerService.java @@ -4404,8 +4404,11 @@ public final class PowerManagerService extends SystemService @Override // Binder call public boolean setPowerSaveMode(boolean enabled) { - mContext.enforceCallingOrSelfPermission( - android.Manifest.permission.DEVICE_POWER, null); + if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.POWER_SAVER) + != PackageManager.PERMISSION_GRANTED) { + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.DEVICE_POWER, null); + } final long ident = Binder.clearCallingIdentity(); try { return setLowPowerModeInternal(enabled); diff --git a/services/core/java/com/android/server/security/VerityUtils.java b/services/core/java/com/android/server/security/VerityUtils.java index 37966108fe64..8070f3add5c6 100644 --- a/services/core/java/com/android/server/security/VerityUtils.java +++ b/services/core/java/com/android/server/security/VerityUtils.java @@ -26,9 +26,9 @@ import android.system.Os; import android.util.Pair; import android.util.Slog; import android.util.apk.ApkSignatureVerifier; -import android.util.apk.ApkVerityBuilder; import android.util.apk.ByteBufferFactory; import android.util.apk.SignatureNotFoundException; +import android.util.apk.VerityBuilder; import libcore.util.HexEncoding; @@ -115,9 +115,9 @@ abstract public class VerityUtils { } /** - * {@see ApkSignatureVerifier#generateFsverityRootHash(String)}. + * {@see ApkSignatureVerifier#generateApkVerityRootHash(String)}. */ - public static byte[] generateFsverityRootHash(@NonNull String apkPath) + public static byte[] generateApkVerityRootHash(@NonNull String apkPath) throws NoSuchAlgorithmException, DigestException, IOException { return ApkSignatureVerifier.generateApkVerityRootHash(apkPath); } @@ -146,7 +146,7 @@ abstract public class VerityUtils { throws IOException, SignatureNotFoundException, SecurityException, DigestException, NoSuchAlgorithmException { try (RandomAccessFile file = new RandomAccessFile(filePath, "r")) { - ApkVerityBuilder.ApkVerityResult result = ApkVerityBuilder.generateFsVerityTree( + VerityBuilder.VerityResult result = VerityBuilder.generateFsVerityTree( file, trackedBufferFactory); ByteBuffer buffer = result.verityData; diff --git a/services/core/java/com/android/server/stats/StatsCompanionService.java b/services/core/java/com/android/server/stats/StatsCompanionService.java index c6e6449313c0..d0de9409c49e 100644 --- a/services/core/java/com/android/server/stats/StatsCompanionService.java +++ b/services/core/java/com/android/server/stats/StatsCompanionService.java @@ -47,6 +47,7 @@ import android.os.IStatsManager; import android.os.IStoraged; import android.os.IThermalEventListener; import android.os.IThermalService; +import android.os.ParcelFileDescriptor; import android.os.Parcelable; import android.os.Process; import android.os.RemoteException; @@ -63,10 +64,13 @@ import android.os.storage.StorageManager; import android.telephony.ModemActivityInfo; import android.telephony.TelephonyManager; import android.util.ArrayMap; +import android.util.Log; import android.util.Slog; import android.util.StatsLog; import com.android.internal.annotations.GuardedBy; +import com.android.internal.app.procstats.IProcessStats; +import com.android.internal.app.procstats.ProcessStats; import com.android.internal.net.NetworkStatsFactory; import com.android.internal.os.BinderCallsStats.ExportedCallStat; import com.android.internal.os.KernelCpuSpeedReader; @@ -78,10 +82,12 @@ import com.android.internal.os.KernelWakelockReader; import com.android.internal.os.KernelWakelockStats; import com.android.internal.os.LooperStats; import com.android.internal.os.PowerProfile; +import com.android.internal.os.StoragedUidIoStatsReader; import com.android.internal.util.DumpUtils; import com.android.server.BinderCallsStatsService; import com.android.server.LocalServices; import com.android.server.SystemService; +import com.android.server.SystemServiceManager; import com.android.server.storage.DiskStatsFileLogger; import com.android.server.storage.DiskStatsLoggingService; @@ -95,6 +101,7 @@ import java.io.File; import java.io.FileDescriptor; import java.io.FileOutputStream; import java.io.IOException; +import java.io.InputStream; import java.io.PrintWriter; import java.util.ArrayList; import java.util.Arrays; @@ -123,7 +130,7 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { public static final String CONFIG_DIR = "/data/misc/stats-service"; static final String TAG = "StatsCompanionService"; - static final boolean DEBUG = true; + static final boolean DEBUG = false; public static final int CODE_DATA_BROADCAST = 1; public static final int CODE_SUBSCRIBER_BROADCAST = 1; @@ -172,14 +179,18 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { new KernelUidCpuActiveTimeReader(); private KernelUidCpuClusterTimeReader mKernelUidCpuClusterTimeReader = new KernelUidCpuClusterTimeReader(); + private StoragedUidIoStatsReader mStoragedUidIoStatsReader = + new StoragedUidIoStatsReader(); private static IThermalService sThermalService; + private File mBaseDir = + new File(SystemServiceManager.ensureSystemDir(), "stats_companion"); public StatsCompanionService(Context context) { super(); mContext = context; mAlarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE); - + mBaseDir.mkdirs(); mAppUpdateReceiver = new AppUpdateReceiver(); mUserUpdateReceiver = new BroadcastReceiver() { @Override @@ -1245,6 +1256,107 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { Binder.restoreCallingIdentity(token); } + long mLastProcStatsHighWaterMark = readProcStatsHighWaterMark(); + + private long readProcStatsHighWaterMark() { + try { + File[] files = mBaseDir.listFiles(); + if (files == null || files.length == 0) { + return 0; + } + if (files.length > 1) { + Log.e(TAG, "Only 1 file expected for high water mark. Found " + files.length); + } + return Long.valueOf(files[0].getName()); + } catch (SecurityException e) { + Log.e(TAG, "Failed to get procstats high watermark file.", e); + } catch (NumberFormatException e) { + Log.e(TAG, "Failed to parse file name.", e); + } + return 0; + } + + private IProcessStats mProcessStats = + IProcessStats.Stub.asInterface(ServiceManager.getService(ProcessStats.SERVICE_NAME)); + + private void pullProcessStats( + int tagId, long elapsedNanos, long wallClockNanos, + List<StatsLogEventWrapper> pulledData) { + try { + List<ParcelFileDescriptor> statsFiles = new ArrayList<>(); + long highWaterMark = mProcessStats.getCommittedStats( + mLastProcStatsHighWaterMark, ProcessStats.REPORT_ALL, true, statsFiles); + if (statsFiles.size() != 1) { + return; + } + InputStream stream = new ParcelFileDescriptor.AutoCloseInputStream(statsFiles.get(0)); + int[] len = new int[1]; + byte[] stats = readFully(stream, len); + StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos); + e.writeStorage(Arrays.copyOf(stats, len[0])); + pulledData.add(e); + new File(mBaseDir.getAbsolutePath() + "/" + mLastProcStatsHighWaterMark).delete(); + mLastProcStatsHighWaterMark = highWaterMark; + new File( + mBaseDir.getAbsolutePath() + "/" + mLastProcStatsHighWaterMark).createNewFile(); + } catch (IOException e) { + Log.e(TAG, "Getting procstats failed: ", e); + } catch (RemoteException e) { + Log.e(TAG, "Getting procstats failed: ", e); + } catch (SecurityException e) { + Log.e(TAG, "Getting procstats failed: ", e); + } + } + + static byte[] readFully(InputStream stream, int[] outLen) throws IOException { + int pos = 0; + final int initialAvail = stream.available(); + byte[] data = new byte[initialAvail > 0 ? (initialAvail + 1) : 16384]; + while (true) { + int amt = stream.read(data, pos, data.length - pos); + if (DEBUG) { + Slog.i(TAG, "Read " + amt + " bytes at " + pos + " of avail " + data.length); + } + if (amt < 0) { + if (DEBUG) { + Slog.i(TAG, "**** FINISHED READING: pos=" + pos + " len=" + data.length); + } + outLen[0] = pos; + return data; + } + pos += amt; + if (pos >= data.length) { + byte[] newData = new byte[pos + 16384]; + if (DEBUG) { + Slog.i(TAG, "Copying " + pos + " bytes to new array len " + newData.length); + } + System.arraycopy(data, 0, newData, 0, pos); + data = newData; + } + } + } + + private void pullDiskIo(int tagId, long elapsedNanos, final long wallClockNanos, + List<StatsLogEventWrapper> pulledData) { + mStoragedUidIoStatsReader.readAbsolute((uid, fgCharsRead, fgCharsWrite, fgBytesRead, + fgBytesWrite, bgCharsRead, bgCharsWrite, bgBytesRead, bgBytesWrite, + fgFsync, bgFsync) -> { + StatsLogEventWrapper e = new StatsLogEventWrapper(tagId, elapsedNanos, wallClockNanos); + e.writeInt(uid); + e.writeLong(fgCharsRead); + e.writeLong(fgCharsWrite); + e.writeLong(fgBytesRead); + e.writeLong(fgBytesWrite); + e.writeLong(bgCharsRead); + e.writeLong(bgCharsWrite); + e.writeLong(bgBytesRead); + e.writeLong(bgBytesWrite); + e.writeLong(fgFsync); + e.writeLong(bgFsync); + pulledData.add(e); + }); + } + /** * Pulls various data. */ @@ -1358,6 +1470,14 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { pullNumFingerprints(tagId, elapsedNanos, wallClockNanos, ret); break; } + case StatsLog.PROC_STATS: { + pullProcessStats(tagId, elapsedNanos, wallClockNanos, ret); + break; + } + case StatsLog.DISK_IO: { + pullDiskIo(tagId, elapsedNanos, wallClockNanos, ret); + break; + } default: Slog.w(TAG, "No such tagId data as " + tagId); return null; @@ -1368,13 +1488,13 @@ public class StatsCompanionService extends IStatsCompanionService.Stub { @Override // Binder call public void statsdReady() { enforceCallingPermission(); - if (DEBUG) Slog.d(TAG, "learned that statsdReady"); + if (DEBUG) { + Slog.d(TAG, "learned that statsdReady"); + } sayHiToStatsd(); // tell statsd that we're ready too and link to it - mContext.sendBroadcastAsUser( - new Intent(StatsManager.ACTION_STATSD_STARTED) + mContext.sendBroadcastAsUser(new Intent(StatsManager.ACTION_STATSD_STARTED) .addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND), - UserHandle.SYSTEM, - android.Manifest.permission.DUMP); + UserHandle.SYSTEM, android.Manifest.permission.DUMP); } @Override diff --git a/services/core/java/com/android/server/wm/AnimationAdapter.java b/services/core/java/com/android/server/wm/AnimationAdapter.java index 00e30507d232..be8a0bd7ad32 100644 --- a/services/core/java/com/android/server/wm/AnimationAdapter.java +++ b/services/core/java/com/android/server/wm/AnimationAdapter.java @@ -35,12 +35,6 @@ interface AnimationAdapter { long STATUS_BAR_TRANSITION_DURATION = 120L; /** - * @return Whether we should detach the wallpaper during the animation. - * @see Animation#setDetachWallpaper - */ - boolean getDetachWallpaper(); - - /** * @return Whether we should show the wallpaper during the animation. * @see Animation#getShowWallpaper() */ diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java index 6da9f104a212..fc7610239fa3 100644 --- a/services/core/java/com/android/server/wm/AppWindowToken.java +++ b/services/core/java/com/android/server/wm/AppWindowToken.java @@ -1632,17 +1632,6 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree return null; } - int getLowestAnimLayer() { - for (int i = 0; i < mChildren.size(); i++) { - final WindowState w = mChildren.get(i); - if (w.mRemoved) { - continue; - } - return w.mWinAnimator.mAnimLayer; - } - return Integer.MAX_VALUE; - } - WindowState getHighestAnimLayerWindow(WindowState currentTarget) { WindowState candidate = null; for (int i = mChildren.indexOf(currentTarget); i >= 0; i--) { @@ -1650,8 +1639,7 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree if (w.mRemoved) { continue; } - if (candidate == null || w.mWinAnimator.mAnimLayer > - candidate.mWinAnimator.mAnimLayer) { + if (candidate == null) { candidate = w; } } diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index 236982fd76ac..a762fe9362c5 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -439,36 +439,12 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo return; } - final int flags = w.mAttrs.flags; - - // If this window is animating, make a note that we have an animating window and take - // care of a request to run a detached wallpaper animation. - if (winAnimator.isAnimationSet()) { - final AnimationAdapter anim = w.getAnimation(); - if (anim != null) { - if ((flags & FLAG_SHOW_WALLPAPER) != 0 && anim.getDetachWallpaper()) { - mTmpWindow = w; - } - final int color = anim.getBackgroundColor(); - if (color != 0) { - final TaskStack stack = w.getStack(); - if (stack != null) { - stack.setAnimationBackground(winAnimator, color); - } - } - } - } - - // If this window's app token is running a detached wallpaper animation, make a note so - // we can ensure the wallpaper is displayed behind it. - final AppWindowToken atoken = winAnimator.mWin.mAppToken; - final AnimationAdapter animation = atoken != null ? atoken.getAnimation() : null; - if (animation != null) { - if ((flags & FLAG_SHOW_WALLPAPER) != 0 && animation.getDetachWallpaper()) { - mTmpWindow = w; - } - - final int color = animation.getBackgroundColor(); + // If this window is animating, ensure the animation background is set. + final AnimationAdapter anim = w.mAppToken != null + ? w.mAppToken.getAnimation() + : w.getAnimation(); + if (anim != null) { + final int color = anim.getBackgroundColor(); if (color != 0) { final TaskStack stack = w.getStack(); if (stack != null) { @@ -2307,21 +2283,6 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo mPinnedStackControllerLocked.setAdjustedForIme(imeVisible, imeHeight); } - /** - * If a window that has an animation specifying a colored background and the current wallpaper - * is visible, then the color goes *below* the wallpaper so we don't cause the wallpaper to - * suddenly disappear. - */ - int getLayerForAnimationBackground(WindowStateAnimator winAnimator) { - final WindowState visibleWallpaper = mBelowAppWindowsContainers.getWindow( - w -> w.mIsWallpaper && w.isVisibleNow()); - - if (visibleWallpaper != null) { - return visibleWallpaper.mWinAnimator.mAnimLayer; - } - return winAnimator.mAnimLayer; - } - void prepareFreezingTaskBounds() { for (int stackNdx = mTaskStackContainers.getChildCount() - 1; stackNdx >= 0; --stackNdx) { final TaskStack stack = mTaskStackContainers.getChildAt(stackNdx); @@ -2746,22 +2707,13 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo if (highestTarget != null) { final AppTransition appTransition = mService.mAppTransition; if (DEBUG_INPUT_METHOD) Slog.v(TAG_WM, appTransition + " " + highestTarget - + " animating=" + highestTarget.mWinAnimator.isAnimationSet() - + " layer=" + highestTarget.mWinAnimator.mAnimLayer - + " new layer=" + target.mWinAnimator.mAnimLayer); + + " animating=" + highestTarget.isAnimating()); if (appTransition.isTransitionSet()) { // If we are currently setting up for an animation, hold everything until we // can find out what will happen. setInputMethodTarget(highestTarget, true); return highestTarget; - } else if (highestTarget.mWinAnimator.isAnimationSet() && - highestTarget.mWinAnimator.mAnimLayer > target.mWinAnimator.mAnimLayer) { - // If the window we are currently targeting is involved with an animation, - // and it is on top of the next target we will be over, then hold off on - // moving until that is done. - setInputMethodTarget(highestTarget, true); - return highestTarget; } } } @@ -2934,26 +2886,16 @@ class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowCo return false; } - void updateWindowsForAnimator(WindowAnimator animator) { - mTmpWindowAnimator = animator; + void updateWindowsForAnimator() { forAllWindows(mUpdateWindowsForAnimator, true /* traverseTopToBottom */); } - void updateWallpaperForAnimator(WindowAnimator animator) { + /** + * Updates the {@link TaskStack#setAnimationBackground} for all windows. + */ + void updateBackgroundForAnimator() { resetAnimationBackgroundAnimator(); - - // Used to indicate a detached wallpaper. - mTmpWindow = null; - mTmpWindowAnimator = animator; - forAllWindows(mUpdateWallpaperForAnimator, true /* traverseTopToBottom */); - - if (animator.mWindowDetachedWallpaper != mTmpWindow) { - if (DEBUG_WALLPAPER) Slog.v(TAG, "Detached wallpaper changed from " - + animator.mWindowDetachedWallpaper + " to " + mTmpWindow); - animator.mWindowDetachedWallpaper = mTmpWindow; - animator.mBulkUpdateParams |= SET_WALLPAPER_MAY_CHANGE; - } } boolean isInputMethodClientFocus(int uid, int pid) { diff --git a/services/core/java/com/android/server/wm/LocalAnimationAdapter.java b/services/core/java/com/android/server/wm/LocalAnimationAdapter.java index d89d6f056218..77a024cc2e99 100644 --- a/services/core/java/com/android/server/wm/LocalAnimationAdapter.java +++ b/services/core/java/com/android/server/wm/LocalAnimationAdapter.java @@ -44,11 +44,6 @@ class LocalAnimationAdapter implements AnimationAdapter { } @Override - public boolean getDetachWallpaper() { - return mSpec.getDetachWallpaper(); - } - - @Override public boolean getShowWallpaper() { return mSpec.getShowWallpaper(); } @@ -98,13 +93,6 @@ class LocalAnimationAdapter implements AnimationAdapter { interface AnimationSpec { /** - * @see AnimationAdapter#getDetachWallpaper - */ - default boolean getDetachWallpaper() { - return false; - } - - /** * @see AnimationAdapter#getShowWallpaper */ default boolean getShowWallpaper() { diff --git a/services/core/java/com/android/server/wm/RecentsAnimationController.java b/services/core/java/com/android/server/wm/RecentsAnimationController.java index e718c7b2a36e..6fef16304d42 100644 --- a/services/core/java/com/android/server/wm/RecentsAnimationController.java +++ b/services/core/java/com/android/server/wm/RecentsAnimationController.java @@ -609,11 +609,6 @@ public class RecentsAnimationController implements DeathRecipient { } @Override - public boolean getDetachWallpaper() { - return false; - } - - @Override public boolean getShowWallpaper() { return false; } diff --git a/services/core/java/com/android/server/wm/RemoteAnimationController.java b/services/core/java/com/android/server/wm/RemoteAnimationController.java index 00422e3497be..8ec0a014e4a9 100644 --- a/services/core/java/com/android/server/wm/RemoteAnimationController.java +++ b/services/core/java/com/android/server/wm/RemoteAnimationController.java @@ -325,11 +325,6 @@ class RemoteAnimationController implements DeathRecipient { } @Override - public boolean getDetachWallpaper() { - return false; - } - - @Override public boolean getShowWallpaper() { return false; } diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java index 2b8493749c79..00cacebe2960 100644 --- a/services/core/java/com/android/server/wm/TaskStack.java +++ b/services/core/java/com/android/server/wm/TaskStack.java @@ -1070,11 +1070,8 @@ public class TaskStack extends WindowContainer<Task> implements } void setAnimationBackground(WindowStateAnimator winAnimator, int color) { - int animLayer = winAnimator.mAnimLayer; - if (mAnimationBackgroundAnimator == null - || animLayer < mAnimationBackgroundAnimator.mAnimLayer) { + if (mAnimationBackgroundAnimator == null) { mAnimationBackgroundAnimator = winAnimator; - animLayer = mDisplayContent.getLayerForAnimationBackground(winAnimator); showAnimationSurface(((color >> 24) & 0xff) / 255f); } } diff --git a/services/core/java/com/android/server/wm/WallpaperController.java b/services/core/java/com/android/server/wm/WallpaperController.java index 3d349ce34d6b..a448f97306f0 100644 --- a/services/core/java/com/android/server/wm/WallpaperController.java +++ b/services/core/java/com/android/server/wm/WallpaperController.java @@ -120,13 +120,11 @@ class WallpaperController { } mFindResults.resetTopWallpaper = true; - if (w != winAnimator.mWindowDetachedWallpaper && w.mAppToken != null) { + if (w.mAppToken != null && w.mAppToken.isHidden() && !w.mAppToken.isSelfAnimating()) { + // If this window's app token is hidden and not animating, it is of no interest to us. - if (w.mAppToken.isHidden() && !w.mAppToken.isSelfAnimating()) { - if (DEBUG_WALLPAPER) Slog.v(TAG, - "Skipping hidden and not animating token: " + w); - return false; - } + if (DEBUG_WALLPAPER) Slog.v(TAG, "Skipping hidden and not animating token: " + w); + return false; } if (DEBUG_WALLPAPER) Slog.v(TAG, "Win " + w + ": isOnScreen=" + w.isOnScreen() + " mDrawState=" + w.mWinAnimator.mDrawState); @@ -177,7 +175,7 @@ class WallpaperController { && (mWallpaperTarget == w || w.isDrawFinishedLw())) { if (DEBUG_WALLPAPER) Slog.v(TAG, "Found wallpaper target: " + w); mFindResults.setWallpaperTarget(w); - if (w == mWallpaperTarget && w.mWinAnimator.isAnimationSet()) { + if (w == mWallpaperTarget && w.isAnimating()) { // The current wallpaper target is animating, so we'll look behind it for // another possible target and figure out what is going on later. if (DEBUG_WALLPAPER) Slog.v(TAG, @@ -185,10 +183,6 @@ class WallpaperController { } // Found a target! End search. return true; - } else if (w == winAnimator.mWindowDetachedWallpaper) { - if (DEBUG_WALLPAPER_LIGHT) Slog.v(TAG, - "Found animating detached wallpaper target win: " + w); - mFindResults.setUseTopWallpaperAsTarget(true); } return false; }; @@ -243,7 +237,7 @@ class WallpaperController { } boolean isWallpaperTargetAnimating() { - return mWallpaperTarget != null && mWallpaperTarget.mWinAnimator.isAnimationSet() + return mWallpaperTarget != null && mWallpaperTarget.isAnimating() && (mWallpaperTarget.mAppToken == null || !mWallpaperTarget.mAppToken.isWaitingForTransitionStart()); } diff --git a/services/core/java/com/android/server/wm/WallpaperWindowToken.java b/services/core/java/com/android/server/wm/WallpaperWindowToken.java index ddda027595da..e15b783b5606 100644 --- a/services/core/java/com/android/server/wm/WallpaperWindowToken.java +++ b/services/core/java/com/android/server/wm/WallpaperWindowToken.java @@ -138,7 +138,7 @@ class WallpaperWindowToken extends WindowToken { wallpaper.dispatchWallpaperVisibility(visible); if (DEBUG_LAYERS || DEBUG_WALLPAPER_LIGHT) Slog.v(TAG, "adjustWallpaper win " - + wallpaper + " anim layer: " + wallpaper.mWinAnimator.mAnimLayer); + + wallpaper); } } diff --git a/services/core/java/com/android/server/wm/WindowAnimationSpec.java b/services/core/java/com/android/server/wm/WindowAnimationSpec.java index 825255e556ff..98c77ac719f9 100644 --- a/services/core/java/com/android/server/wm/WindowAnimationSpec.java +++ b/services/core/java/com/android/server/wm/WindowAnimationSpec.java @@ -72,11 +72,6 @@ public class WindowAnimationSpec implements AnimationSpec { } @Override - public boolean getDetachWallpaper() { - return mAnimation.getDetachWallpaper(); - } - - @Override public boolean getShowWallpaper() { return mAnimation.getShowWallpaper(); } diff --git a/services/core/java/com/android/server/wm/WindowAnimator.java b/services/core/java/com/android/server/wm/WindowAnimator.java index a1d6ffd0249e..ad0b8ece7a80 100644 --- a/services/core/java/com/android/server/wm/WindowAnimator.java +++ b/services/core/java/com/android/server/wm/WindowAnimator.java @@ -58,17 +58,6 @@ public class WindowAnimator { /** Time of current animation step. Reset on each iteration */ long mCurrentTime; - boolean mAppWindowAnimating; - /** Skip repeated AppWindowTokens initialization. Note that AppWindowsToken's version of this - * is a long initialized to Long.MIN_VALUE so that it doesn't match this value on startup. */ - int mAnimTransactionSequence; - - /** Window currently running an animation that has requested it be detached - * from the wallpaper. This means we need to ensure the wallpaper is - * visible behind it in case it animates in a way that would allow it to be - * seen. If multiple windows satisfy this, use the lowest window. */ - WindowState mWindowDetachedWallpaper = null; - int mBulkUpdateParams = 0; Object mLastWindowFreezeSource; @@ -191,9 +180,8 @@ public class WindowAnimator { // Update animations of all applications, including those // associated with exiting/removed apps - ++mAnimTransactionSequence; - dc.updateWindowsForAnimator(this); - dc.updateWallpaperForAnimator(this); + dc.updateWindowsForAnimator(); + dc.updateBackgroundForAnimator(); dc.prepareSurfaces(); } @@ -314,8 +302,6 @@ public class WindowAnimator { pw.println(); if (dumpAll) { - pw.print(prefix); pw.print("mAnimTransactionSequence="); - pw.print(mAnimTransactionSequence); pw.print(prefix); pw.print("mCurrentTime="); pw.println(TimeUtils.formatUptime(mCurrentTime)); } @@ -324,10 +310,6 @@ public class WindowAnimator { pw.print(Integer.toHexString(mBulkUpdateParams)); pw.println(bulkUpdateParamsToString(mBulkUpdateParams)); } - if (mWindowDetachedWallpaper != null) { - pw.print(prefix); pw.print("mWindowDetachedWallpaper="); - pw.println(mWindowDetachedWallpaper); - } } int getPendingLayoutChanges(final int displayId) { diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index b627df4a3313..942e47b4725f 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -2209,7 +2209,7 @@ public class WindowManagerService extends IWindowManager.Stub if (win.isWinVisibleLw() && winAnimator.applyAnimationLocked(transit, false)) { focusMayChange = isDefaultDisplay; win.mAnimatingExit = true; - } else if (win.mWinAnimator.isAnimationSet()) { + } else if (win.isAnimating()) { // Currently in a hide animation... turn this into // an exit. win.mAnimatingExit = true; @@ -5574,11 +5574,7 @@ public class WindowManagerService extends IWindowManager.Stub if (mode != UPDATE_FOCUS_WILL_ASSIGN_LAYERS && mode != UPDATE_FOCUS_WILL_PLACE_SURFACES) { - final int prevImeAnimLayer = - displayContent.mInputMethodWindow.mWinAnimator.mAnimLayer; displayContent.assignWindowLayers(false /* setLayoutNeeded */); - imWindowChanged |= prevImeAnimLayer - != displayContent.mInputMethodWindow.mWinAnimator.mAnimLayer; } } diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index f7c6d77b4163..a4bac31bbcee 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -1364,7 +1364,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP @Override boolean hasContentToDisplay() { if (!mAppFreezing && isDrawnLw() && (mViewVisibility == View.VISIBLE - || (mWinAnimator.isAnimationSet() && !mService.mAppTransition.isTransitionSet()))) { + || (isAnimating() && !mService.mAppTransition.isTransitionSet()))) { return true; } @@ -1443,9 +1443,9 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP final AppWindowToken atoken = mAppToken; if (atoken != null) { return ((!isParentWindowHidden() && !atoken.hiddenRequested) - || mWinAnimator.isAnimationSet()); + || isAnimating()); } - return !isParentWindowHidden() || mWinAnimator.isAnimationSet(); + return !isParentWindowHidden() || isAnimating(); } /** @@ -1476,9 +1476,10 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP if (mToken.waitingToShow && mService.mAppTransition.isTransitionSet()) { return false; } + final boolean parentAndClientVisible = !isParentWindowHidden() + && mViewVisibility == View.VISIBLE && !mToken.isHidden(); return mHasSurface && mPolicyVisibility && !mDestroying - && ((!isParentWindowHidden() && mViewVisibility == View.VISIBLE && !mToken.isHidden()) - || mWinAnimator.isAnimationSet()); + && (parentAndClientVisible || isAnimating()); } // TODO: Another visibility method that was added late in the release to minimize risk. @@ -1508,7 +1509,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP final AppWindowToken atoken = mAppToken; return isDrawnLw() && mPolicyVisibility && ((!isParentWindowHidden() && (atoken == null || !atoken.hiddenRequested)) - || mWinAnimator.isAnimationSet()); + || isAnimating()); } /** @@ -1562,7 +1563,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP // to determine if it's occluding apps. return ((!mIsWallpaper && mAttrs.format == PixelFormat.OPAQUE) || (mIsWallpaper && mWallpaperVisible)) - && isDrawnLw() && !mWinAnimator.isAnimationSet(); + && isDrawnLw() && !isAnimating(); } @Override @@ -1849,7 +1850,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP + " mRemoveOnExit=" + mRemoveOnExit + " mHasSurface=" + mHasSurface + " surfaceShowing=" + mWinAnimator.getShown() - + " isAnimationSet=" + mWinAnimator.isAnimationSet() + + " animating=" + isAnimating() + " app-animation=" + (mAppToken != null ? mAppToken.isSelfAnimating() : "false") + " mWillReplaceWindow=" + mWillReplaceWindow @@ -1916,7 +1917,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP mService.mAccessibilityController.onWindowTransitionLocked(this, transit); } } - final boolean isAnimating = mWinAnimator.isAnimationSet() + final boolean isAnimating = isAnimating() && (mAppToken == null || !mAppToken.isWaitingForTransitionStart()); final boolean lastWindowIsStartingWindow = startingWindow && mAppToken != null && mAppToken.isLastWindow(this); @@ -2434,10 +2435,10 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP if (DEBUG_VISIBILITY) Slog.v(TAG, "Policy visibility true: " + this); if (doAnimation) { if (DEBUG_VISIBILITY) Slog.v(TAG, "doAnimation: mPolicyVisibility=" - + mPolicyVisibility + " isAnimationSet=" + mWinAnimator.isAnimationSet()); + + mPolicyVisibility + " animating=" + isAnimating()); if (!mToken.okToAnimate()) { doAnimation = false; - } else if (mPolicyVisibility && !mWinAnimator.isAnimationSet()) { + } else if (mPolicyVisibility && !isAnimating()) { // Check for the case where we are currently visible and // not animating; we do not want to do animation at such a // point to become visible when we already are. @@ -2476,7 +2477,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP } if (doAnimation) { mWinAnimator.applyAnimationLocked(TRANSIT_EXIT, false); - if (!mWinAnimator.isAnimationSet()) { + if (!isAnimating()) { doAnimation = false; } } @@ -3216,10 +3217,8 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP + " mWallpaperVisible=" + mWallpaperVisible); } if (dumpAll) { - pw.println(prefix + "mBaseLayer=" + mBaseLayer - + " mSubLayer=" + mSubLayer - + " mAnimLayer=" + mLayer + "=" + mWinAnimator.mAnimLayer - + " mLastLayer=" + mWinAnimator.mLastLayer); + pw.print(prefix); pw.print("mBaseLayer="); pw.print(mBaseLayer); + pw.print(" mSubLayer="); pw.print(mSubLayer); } if (dumpAll) { pw.println(prefix + "mToken=" + mToken); @@ -3697,7 +3696,7 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP + " tok.hiddenRequested=" + (mAppToken != null && mAppToken.hiddenRequested) + " tok.hidden=" + (mAppToken != null && mAppToken.isHidden()) - + " animationSet=" + mWinAnimator.isAnimationSet() + + " animating=" + isAnimating() + " tok animating=" + (mAppToken != null && mAppToken.isSelfAnimating()) + " Callers=" + Debug.getCallers(4)); @@ -3749,18 +3748,6 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP return windowInfo; } - int getHighestAnimLayer() { - int highest = mWinAnimator.mAnimLayer; - for (int i = mChildren.size() - 1; i >= 0; i--) { - final WindowState c = mChildren.get(i); - final int childLayer = c.getHighestAnimLayer(); - if (childLayer > highest) { - highest = childLayer; - } - } - return highest; - } - @Override boolean forAllWindows(ToBooleanFunction<WindowState> callback, boolean traverseTopToBottom) { if (mChildren.isEmpty()) { @@ -4110,25 +4097,25 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP } if (DEBUG_VISIBILITY) { Slog.v(TAG, "Win " + this + ": isDrawn=" + isDrawnLw() - + ", isAnimationSet=" + mWinAnimator.isAnimationSet()); + + ", animating=" + isAnimating()); if (!isDrawnLw()) { Slog.v(TAG, "Not displayed: s=" + mWinAnimator.mSurfaceController + " pv=" + mPolicyVisibility + " mDrawState=" + mWinAnimator.mDrawState + " ph=" + isParentWindowHidden() + " th=" + (mAppToken != null ? mAppToken.hiddenRequested : false) - + " a=" + mWinAnimator.isAnimationSet()); + + " a=" + isAnimating()); } } results.numInteresting++; if (isDrawnLw()) { results.numDrawn++; - if (!mWinAnimator.isAnimationSet()) { + if (!isAnimating()) { results.numVisible++; } results.nowGone = false; - } else if (mWinAnimator.isAnimationSet()) { + } else if (isAnimating()) { results.nowGone = false; } } diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java index 979149a854cb..2beb7887698e 100644 --- a/services/core/java/com/android/server/wm/WindowStateAnimator.java +++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java @@ -107,8 +107,6 @@ class WindowStateAnimator { private final WallpaperController mWallpaperControllerLocked; boolean mAnimationIsEntrance; - int mAnimLayer; - int mLastLayer; /** * Set when we have changed the size of the surface, to know that @@ -135,7 +133,6 @@ class WindowStateAnimator { float mLastAlpha = 0; Rect mTmpClipRect = new Rect(); - Rect mTmpFinalClipRect = new Rect(); Rect mLastClipRect = new Rect(); Rect mLastFinalClipRect = new Rect(); Rect mTmpStackBounds = new Rect(); @@ -162,8 +159,6 @@ class WindowStateAnimator { * window is first added or shown, cleared when the callback has been made. */ boolean mEnteringAnimation; - private boolean mAnimationStartDelayed; - private final SurfaceControl.Transaction mTmpTransaction = new SurfaceControl.Transaction(); /** The pixel format of the underlying SurfaceControl */ @@ -253,13 +248,6 @@ class WindowStateAnimator { mWallpaperControllerLocked = mService.mRoot.mWallpaperController; } - /** - * Is the window or its container currently set to animate or currently animating? - */ - boolean isAnimationSet() { - return mWin.isAnimating(); - } - void cancelExitAnimationForNextAnimationLocked() { if (DEBUG_ANIM) Slog.d(TAG, "cancelExitAnimationForNextAnimationLocked: " + mWin); @@ -275,10 +263,6 @@ class WindowStateAnimator { + ", reportedVisible=" + (mWin.mAppToken != null ? mWin.mAppToken.reportedVisible : false)); - if (mAnimator.mWindowDetachedWallpaper == mWin) { - mAnimator.mWindowDetachedWallpaper = null; - } - mWin.checkPolicyVisibilityChange(); final DisplayContent displayContent = mWin.getDisplayContent(); if (mAttrType == LayoutParams.TYPE_STATUS_BAR && mWin.mPolicyVisibility) { @@ -288,7 +272,6 @@ class WindowStateAnimator { displayContent.setLayoutNeeded(); } } - mWin.onExitAnimationDone(); final int displayId = mWin.getDisplayId(); int pendingLayoutChanges = FINISH_LAYOUT_REDO_ANIM; @@ -539,14 +522,13 @@ class WindowStateAnimator { } if (WindowManagerService.localLOGV) Slog.v(TAG, "Got surface: " + mSurfaceController - + ", set left=" + w.getFrameLw().left + " top=" + w.getFrameLw().top - + ", animLayer=" + mAnimLayer); + + ", set left=" + w.getFrameLw().left + " top=" + w.getFrameLw().top); if (SHOW_LIGHT_TRANSACTIONS) { Slog.i(TAG, ">>> OPEN TRANSACTION createSurfaceLocked"); WindowManagerService.logSurface(w, "CREATE pos=(" + w.getFrameLw().left + "," + w.getFrameLw().top + ") (" - + width + "x" + height + "), layer=" + mAnimLayer + " HIDE", false); + + width + "x" + height + ")" + " HIDE", false); } mLastHidden = true; @@ -1133,8 +1115,7 @@ class WindowStateAnimator { if (DEBUG_ORIENTATION) Slog.v(TAG, "Orientation change skips hidden " + w); } - } else if (mLastLayer != mAnimLayer - || mLastAlpha != mShownAlpha + } else if (mLastAlpha != mShownAlpha || mLastDsDx != mDsDx || mLastDtDx != mDtDx || mLastDsDy != mDsDy @@ -1144,7 +1125,6 @@ class WindowStateAnimator { || mLastHidden) { displayed = true; mLastAlpha = mShownAlpha; - mLastLayer = mAnimLayer; mLastDsDx = mDsDx; mLastDtDx = mDtDx; mLastDsDy = mDsDy; @@ -1153,7 +1133,7 @@ class WindowStateAnimator { w.mLastVScale = w.mVScale; if (SHOW_TRANSACTIONS) WindowManagerService.logSurface(w, "controller=" + mSurfaceController + - "alpha=" + mShownAlpha + " layer=" + mAnimLayer + "alpha=" + mShownAlpha + " matrix=[" + mDsDx + "*" + w.mHScale + "," + mDtDx + "*" + w.mVScale + "][" + mDtDy + "*" + w.mHScale @@ -1197,7 +1177,7 @@ class WindowStateAnimator { w.mToken.hasVisible = true; } } else { - if (DEBUG_ANIM && isAnimationSet()) { + if (DEBUG_ANIM && mWin.isAnimating()) { Slog.v(TAG, "prepareSurface: No changes in animation for " + this); } displayed = true; @@ -1407,7 +1387,7 @@ class WindowStateAnimator { } Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER); - return isAnimationSet(); + return mWin.isAnimating(); } void writeToProto(ProtoOutputStream proto, long fieldId) { @@ -1460,9 +1440,6 @@ class WindowStateAnimator { pw.print(" mDtDy="); pw.print(mDtDy); pw.print(" mDsDy="); pw.println(mDsDy); } - if (mAnimationStartDelayed) { - pw.print(prefix); pw.print("mAnimationStartDelayed="); pw.print(mAnimationStartDelayed); - } } @Override @@ -1520,10 +1497,6 @@ class WindowStateAnimator { mChildrenDetached = true; } - int getLayer() { - return mLastLayer; - } - void setOffsetPositionForStackResize(boolean offsetPositionForStackResize) { mOffsetPositionForStackResize = offsetPositionForStackResize; } diff --git a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java index c8d1a8b14e82..080a3a269947 100644 --- a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java +++ b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java @@ -98,12 +98,6 @@ class WindowSurfacePlacer { private boolean mTraversalScheduled; private int mDeferDepth = 0; - private static final class LayerAndToken { - public int layer; - public AppWindowToken token; - } - private final LayerAndToken mTmpLayerAndToken = new LayerAndToken(); - private final SparseIntArray mTempTransitionReasons = new SparseIntArray(); private final Runnable mPerformSurfacePlacement; @@ -298,10 +292,16 @@ class WindowSurfacePlacer { // done behind a dream window. final ArraySet<Integer> activityTypes = collectActivityTypes(mService.mOpeningApps, mService.mClosingApps); - final AppWindowToken animLpToken = mService.mPolicy.allowAppAnimationsLw() + final boolean allowAnimations = mService.mPolicy.allowAppAnimationsLw(); + final AppWindowToken animLpToken = allowAnimations ? findAnimLayoutParamsToken(transit, activityTypes) : null; - + final AppWindowToken topOpeningApp = allowAnimations + ? getTopApp(mService.mOpeningApps, false /* ignoreHidden */) + : null; + final AppWindowToken topClosingApp = allowAnimations + ? getTopApp(mService.mClosingApps, false /* ignoreHidden */) + : null; final LayoutParams animLp = getAnimLp(animLpToken); overrideWithRemoteAnimationIfSet(animLpToken, transit, activityTypes); @@ -313,17 +313,14 @@ class WindowSurfacePlacer { try { processApplicationsAnimatingInPlace(transit); - mTmpLayerAndToken.token = null; - handleClosingApps(transit, animLp, voiceInteraction, mTmpLayerAndToken); - final AppWindowToken topClosingApp = mTmpLayerAndToken.token; - final AppWindowToken topOpeningApp = handleOpeningApps(transit, animLp, - voiceInteraction); + handleClosingApps(transit, animLp, voiceInteraction); + handleOpeningApps(transit, animLp, voiceInteraction); mService.mAppTransition.setLastAppTransition(transit, topOpeningApp, topClosingApp); final int flags = mService.mAppTransition.getTransitFlags(); - layoutRedo = mService.mAppTransition.goodToGo(transit, topOpeningApp, - topClosingApp, mService.mOpeningApps, mService.mClosingApps); + layoutRedo = mService.mAppTransition.goodToGo(transit, topOpeningApp, topClosingApp, + mService.mOpeningApps, mService.mClosingApps); handleNonAppWindowsInTransition(transit, flags); mService.mAppTransition.postAnimationCallback(); mService.mAppTransition.clear(); @@ -450,10 +447,7 @@ class WindowSurfacePlacer { return false; } - private AppWindowToken handleOpeningApps(int transit, LayoutParams animLp, - boolean voiceInteraction) { - AppWindowToken topOpeningApp = null; - int topOpeningLayer = Integer.MIN_VALUE; + private void handleOpeningApps(int transit, LayoutParams animLp, boolean voiceInteraction) { final int appsCount = mService.mOpeningApps.size(); for (int i = 0; i < appsCount; i++) { AppWindowToken wtoken = mService.mOpeningApps.valueAt(i); @@ -478,24 +472,15 @@ class WindowSurfacePlacer { "<<< CLOSE TRANSACTION handleAppTransitionReadyLocked()"); } - if (animLp != null) { - final int layer = wtoken.getHighestAnimLayer(); - if (topOpeningApp == null || layer > topOpeningLayer) { - topOpeningApp = wtoken; - topOpeningLayer = layer; - } - } if (mService.mAppTransition.isNextAppTransitionThumbnailUp()) { wtoken.attachThumbnailAnimation(); } else if (mService.mAppTransition.isNextAppTransitionOpenCrossProfileApps()) { wtoken.attachCrossProfileAppsThumbnailAnimation(); } } - return topOpeningApp; } - private void handleClosingApps(int transit, LayoutParams animLp, boolean voiceInteraction, - LayerAndToken layerAndToken) { + private void handleClosingApps(int transit, LayoutParams animLp, boolean voiceInteraction) { final int appsCount; appsCount = mService.mClosingApps.size(); for (int i = 0; i < appsCount; i++) { @@ -518,13 +503,6 @@ class WindowSurfacePlacer { wtoken.getController().removeStartingWindow(); } - if (animLp != null) { - int layer = wtoken.getHighestAnimLayer(); - if (layerAndToken.token == null || layer > layerAndToken.layer) { - layerAndToken.token = wtoken; - layerAndToken.layer = layer; - } - } if (mService.mAppTransition.isNextAppTransitionThumbnailDown()) { wtoken.attachThumbnailAnimation(); } diff --git a/services/core/java/com/android/server/wm/WindowToken.java b/services/core/java/com/android/server/wm/WindowToken.java index 8972c3891ec1..fefd305bc6d6 100644 --- a/services/core/java/com/android/server/wm/WindowToken.java +++ b/services/core/java/com/android/server/wm/WindowToken.java @@ -163,7 +163,7 @@ class WindowToken extends WindowContainer<WindowState> { for (int i = 0; i < count; i++) { final WindowState win = mChildren.get(i); - if (win.mWinAnimator.isAnimationSet()) { + if (win.isAnimating()) { delayed = true; } changed |= win.onSetAppExiting(); @@ -235,18 +235,6 @@ class WindowToken extends WindowContainer<WindowState> { return false; } - int getHighestAnimLayer() { - int highest = -1; - for (int j = 0; j < mChildren.size(); j++) { - final WindowState w = mChildren.get(j); - final int wLayer = w.getHighestAnimLayer(); - if (wLayer > highest) { - highest = wLayer; - } - } - return highest; - } - AppWindowToken asAppWindowToken() { // TODO: Not sure if this is the best way to handle this vs. using instanceof and casting. // I am not an app window token! diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp index 061f8e2ba0fb..f7b7f50397e5 100644 --- a/services/core/jni/Android.bp +++ b/services/core/jni/Android.bp @@ -8,6 +8,7 @@ cc_library_static { "-Wall", "-Werror", "-Wno-unused-parameter", + "-Wthread-safety", "-DEGL_EGLEXT_PROTOTYPES", "-DGL_GLEXT_PROTOTYPES", @@ -90,7 +91,6 @@ cc_defaults { "libsensorservicehidl", "libgui", "libusbhost", - "libsuspend", "libtinyalsa", "libEGL", "libGLESv2", @@ -122,6 +122,7 @@ cc_defaults { "android.hardware.vr@1.0", "android.frameworks.schedulerservice@1.0", "android.frameworks.sensorservice@1.0", + "android.system.suspend@1.0", ], static_libs: [ diff --git a/services/core/jni/com_android_server_am_BatteryStatsService.cpp b/services/core/jni/com_android_server_am_BatteryStatsService.cpp index 02ad6c71b586..0ff60e44b0ce 100644 --- a/services/core/jni/com_android_server_am_BatteryStatsService.cpp +++ b/services/core/jni/com_android_server_am_BatteryStatsService.cpp @@ -30,6 +30,8 @@ #include <android/hardware/power/1.0/IPower.h> #include <android/hardware/power/1.1/IPower.h> +#include <android/system/suspend/1.0/ISystemSuspend.h> +#include <android/system/suspend/1.0/ISystemSuspendCallback.h> #include <android_runtime/AndroidRuntime.h> #include <jni.h> @@ -39,7 +41,6 @@ #include <log/log.h> #include <utils/misc.h> #include <utils/Log.h> -#include <suspend/autosuspend.h> using android::hardware::Return; using android::hardware::Void; @@ -49,6 +50,8 @@ using android::hardware::power::V1_0::Status; using android::hardware::power::V1_1::PowerStateSubsystem; using android::hardware::power::V1_1::PowerStateSubsystemSleepState; using android::hardware::hidl_vec; +using android::system::suspend::V1_0::ISystemSuspend; +using android::system::suspend::V1_0::ISystemSuspendCallback; using IPowerV1_1 = android::hardware::power::V1_1::IPower; using IPowerV1_0 = android::hardware::power::V1_0::IPower; @@ -63,6 +66,7 @@ static sem_t wakeup_sem; extern sp<IPowerV1_0> getPowerHalV1_0(); extern sp<IPowerV1_1> getPowerHalV1_1(); extern bool processPowerHalReturn(const Return<void> &ret, const char* functionName); +extern sp<ISystemSuspend> getSuspendHal(); // Java methods used in getLowPowerStats static jmethodID jgetAndUpdatePlatformState = NULL; @@ -70,16 +74,19 @@ static jmethodID jgetSubsystem = NULL; static jmethodID jputVoter = NULL; static jmethodID jputState = NULL; -static void wakeup_callback(bool success) -{ - ALOGV("In wakeup_callback: %s", success ? "resumed from suspend" : "suspend aborted"); - int ret = sem_post(&wakeup_sem); - if (ret < 0) { - char buf[80]; - strerror_r(errno, buf, sizeof(buf)); - ALOGE("Error posting wakeup sem: %s\n", buf); +class WakeupCallback : public ISystemSuspendCallback { +public: + Return<void> notifyWakeup(bool success) override { + ALOGV("In wakeup_callback: %s", success ? "resumed from suspend" : "suspend aborted"); + int ret = sem_post(&wakeup_sem); + if (ret < 0) { + char buf[80]; + strerror_r(errno, buf, sizeof(buf)); + ALOGE("Error posting wakeup sem: %s\n", buf); + } + return Void(); } -} +}; static jint nativeWaitWakeup(JNIEnv *env, jobject clazz, jobject outBuf) { @@ -101,11 +108,14 @@ static jint nativeWaitWakeup(JNIEnv *env, jobject clazz, jobject outBuf) return -1; } ALOGV("Registering callback..."); - autosuspend_set_wakeup_callback(&wakeup_callback); + sp<ISystemSuspend> suspendHal = getSuspendHal(); + suspendHal->registerCallback(new WakeupCallback()); } // Wait for wakeup. ALOGV("Waiting for wakeup..."); + // TODO(b/116747600): device can suspend and wakeup after sem_wait() finishes and before wakeup + // reason is recorded, i.e. BatteryStats might occasionally miss wakeup events. int ret = sem_wait(&wakeup_sem); if (ret < 0) { char buf[80]; diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp index 42ade38fbe51..a754d2aa96b1 100644 --- a/services/core/jni/com_android_server_input_InputManagerService.cpp +++ b/services/core/jni/com_android_server_input_InputManagerService.cpp @@ -64,6 +64,8 @@ #include "com_android_server_input_InputWindowHandle.h" #include "android_hardware_display_DisplayViewport.h" +#include <vector> + #define INDENT " " using android::base::StringPrintf; @@ -144,8 +146,8 @@ static inline const char* toString(bool value) { static jobject getInputApplicationHandleObjLocalRef(JNIEnv* env, const sp<InputApplicationHandle>& inputApplicationHandle) { - if (inputApplicationHandle == NULL) { - return NULL; + if (inputApplicationHandle == nullptr) { + return nullptr; } return static_cast<NativeInputApplicationHandle*>(inputApplicationHandle.get())-> getInputApplicationHandleObjLocalRef(env); @@ -153,8 +155,8 @@ static jobject getInputApplicationHandleObjLocalRef(JNIEnv* env, static jobject getInputWindowHandleObjLocalRef(JNIEnv* env, const sp<InputWindowHandle>& inputWindowHandle) { - if (inputWindowHandle == NULL) { - return NULL; + if (inputWindowHandle == nullptr) { + return nullptr; } return static_cast<NativeInputWindowHandle*>(inputWindowHandle.get())-> getInputWindowHandleObjLocalRef(env); @@ -182,6 +184,15 @@ static void loadSystemIconAsSprite(JNIEnv* env, jobject contextObj, int32_t styl loadSystemIconAsSpriteWithPointerIcon(env, contextObj, style, &pointerIcon, outSpriteIcon); } +static void updatePointerControllerFromViewport( + sp<PointerController> controller, const DisplayViewport* const viewport) { + if (controller != nullptr && viewport != nullptr) { + const int32_t width = viewport->logicalRight - viewport->logicalLeft; + const int32_t height = viewport->logicalBottom - viewport->logicalTop; + controller->setDisplayViewport(width, height, viewport->orientation); + } +} + enum { WM_ACTION_PASS_TO_USER = 1, }; @@ -203,8 +214,7 @@ public: void dump(std::string& dump); - void setVirtualDisplayViewports(JNIEnv* env, jobjectArray viewportObjArray); - void setDisplayViewport(int32_t viewportType, const DisplayViewport& viewport); + void setDisplayViewports(JNIEnv* env, jobjectArray viewportObjArray); status_t registerInputChannel(JNIEnv* env, const sp<InputChannel>& inputChannel, const sp<InputWindowHandle>& inputWindowHandle, bool monitor); @@ -277,9 +287,7 @@ private: Mutex mLock; struct Locked { // Display size information. - DisplayViewport internalViewport; - DisplayViewport externalViewport; - Vector<DisplayViewport> virtualViewports; + std::vector<DisplayViewport> viewports; // System UI visibility. int32_t systemUiVisibility; @@ -304,7 +312,7 @@ private: // Input devices to be disabled SortedVector<int32_t> disabledInputDevices; - } mLocked; + } mLocked GUARDED_BY(mLock); std::atomic<bool> mInteractive; @@ -384,8 +392,17 @@ bool NativeInputManager::checkAndClearExceptionFromCallback(JNIEnv* env, const c return false; } -void NativeInputManager::setVirtualDisplayViewports(JNIEnv* env, jobjectArray viewportObjArray) { - Vector<DisplayViewport> viewports; +static const DisplayViewport* findInternalViewport(const std::vector<DisplayViewport>& viewports) { + for (const DisplayViewport& v : viewports) { + if (v.type == ViewportType::VIEWPORT_INTERNAL) { + return &v; + } + } + return nullptr; +} + +void NativeInputManager::setDisplayViewports(JNIEnv* env, jobjectArray viewportObjArray) { + std::vector<DisplayViewport> viewports; if (viewportObjArray) { jsize length = env->GetArrayLength(viewportObjArray); @@ -397,57 +414,32 @@ void NativeInputManager::setVirtualDisplayViewports(JNIEnv* env, jobjectArray vi DisplayViewport viewport; android_hardware_display_DisplayViewport_toNative(env, viewportObj, &viewport); - ALOGI("Viewport [%d] to add: %s", (int) length, viewport.uniqueId.c_str()); - viewports.push(viewport); + ALOGI("Viewport [%d] to add: %s", (int) i, viewport.uniqueId.c_str()); + viewports.push_back(viewport); env->DeleteLocalRef(viewportObj); } } + const DisplayViewport* newInternalViewport = findInternalViewport(viewports); { AutoMutex _l(mLock); - mLocked.virtualViewports = viewports; + const DisplayViewport* oldInternalViewport = findInternalViewport(mLocked.viewports); + // Internal viewport has changed if there wasn't one earlier, and there is one now, or, + // if they are different. + const bool internalViewportChanged = (newInternalViewport != nullptr) && + (oldInternalViewport == nullptr || (*newInternalViewport != *newInternalViewport)); + if (internalViewportChanged) { + sp<PointerController> controller = mLocked.pointerController.promote(); + updatePointerControllerFromViewport(controller, newInternalViewport); + } + mLocked.viewports = viewports; } mInputManager->getReader()->requestRefreshConfiguration( InputReaderConfiguration::CHANGE_DISPLAY_INFO); } -void NativeInputManager::setDisplayViewport(int32_t type, const DisplayViewport& viewport) { - bool changed = false; - { - AutoMutex _l(mLock); - - ViewportType viewportType = static_cast<ViewportType>(type); - DisplayViewport* v = NULL; - if (viewportType == ViewportType::VIEWPORT_EXTERNAL) { - v = &mLocked.externalViewport; - } else if (viewportType == ViewportType::VIEWPORT_INTERNAL) { - v = &mLocked.internalViewport; - } - - if (v != NULL && *v != viewport) { - changed = true; - *v = viewport; - - if (viewportType == ViewportType::VIEWPORT_INTERNAL) { - sp<PointerController> controller = mLocked.pointerController.promote(); - if (controller != NULL) { - controller->setDisplayViewport( - viewport.logicalRight - viewport.logicalLeft, - viewport.logicalBottom - viewport.logicalTop, - viewport.orientation); - } - } - } - } - - if (changed) { - mInputManager->getReader()->requestRefreshConfiguration( - InputReaderConfiguration::CHANGE_DISPLAY_INFO); - } -} - status_t NativeInputManager::registerInputChannel(JNIEnv* /* env */, const sp<InputChannel>& inputChannel, const sp<InputWindowHandle>& inputWindowHandle, bool monitor) { @@ -479,7 +471,7 @@ void NativeInputManager::getReaderConfiguration(InputReaderConfiguration* outCon jsize length = env->GetArrayLength(excludedDeviceNames); for (jsize i = 0; i < length; i++) { jstring item = jstring(env->GetObjectArrayElement(excludedDeviceNames, i)); - const char* deviceNameChars = env->GetStringUTFChars(item, NULL); + const char* deviceNameChars = env->GetStringUTFChars(item, nullptr); outConfig->excludedDeviceNames.push_back(deviceNameChars); env->ReleaseStringUTFChars(item, deviceNameChars); env->DeleteLocalRef(item); @@ -526,11 +518,7 @@ void NativeInputManager::getReaderConfiguration(InputReaderConfiguration* outCon outConfig->pointerCapture = mLocked.pointerCapture; - outConfig->setPhysicalDisplayViewport(ViewportType::VIEWPORT_INTERNAL, - mLocked.internalViewport); - outConfig->setPhysicalDisplayViewport(ViewportType::VIEWPORT_EXTERNAL, - mLocked.externalViewport); - outConfig->setVirtualDisplayViewports(mLocked.virtualViewports); + outConfig->setDisplayViewports(mLocked.viewports); outConfig->disabledDevices = mLocked.disabledInputDevices; } // release lock @@ -541,25 +529,22 @@ sp<PointerControllerInterface> NativeInputManager::obtainPointerController(int32 AutoMutex _l(mLock); sp<PointerController> controller = mLocked.pointerController.promote(); - if (controller == NULL) { + if (controller == nullptr) { ensureSpriteControllerLocked(); controller = new PointerController(this, mLooper, mLocked.spriteController); mLocked.pointerController = controller; - DisplayViewport& v = mLocked.internalViewport; - controller->setDisplayViewport( - v.logicalRight - v.logicalLeft, - v.logicalBottom - v.logicalTop, - v.orientation); + const DisplayViewport* internalViewport = findInternalViewport(mLocked.viewports); + updatePointerControllerFromViewport(controller, internalViewport); updateInactivityTimeoutLocked(controller); } return controller; } -void NativeInputManager::ensureSpriteControllerLocked() { - if (mLocked.spriteController == NULL) { +void NativeInputManager::ensureSpriteControllerLocked() REQUIRES(mLock) { + if (mLocked.spriteController == nullptr) { JNIEnv* env = jniEnv(); jint layer = env->CallIntMethod(mServiceObj, gServiceClassInfo.getPointerLayer); if (checkAndClearExceptionFromCallback(env, "getPointerLayer")) { @@ -575,7 +560,7 @@ void NativeInputManager::notifyInputDevicesChanged(const Vector<InputDeviceInfo> size_t count = inputDevices.size(); jobjectArray inputDevicesObjArray = env->NewObjectArray( - count, gInputDeviceClassInfo.clazz, NULL); + count, gInputDeviceClassInfo.clazz, nullptr); if (inputDevicesObjArray) { bool error = false; for (size_t i = 0; i < count; i++) { @@ -750,7 +735,7 @@ void NativeInputManager::setInputWindows(JNIEnv* env, jobjectArray windowHandleO sp<InputWindowHandle> windowHandle = android_server_InputWindowHandle_getHandle(env, windowHandleObj); - if (windowHandle != NULL) { + if (windowHandle != nullptr) { windowHandles.push(windowHandle); } env->DeleteLocalRef(windowHandleObj); @@ -803,13 +788,14 @@ void NativeInputManager::setSystemUiVisibility(int32_t visibility) { mLocked.systemUiVisibility = visibility; sp<PointerController> controller = mLocked.pointerController.promote(); - if (controller != NULL) { + if (controller != nullptr) { updateInactivityTimeoutLocked(controller); } } } -void NativeInputManager::updateInactivityTimeoutLocked(const sp<PointerController>& controller) { +void NativeInputManager::updateInactivityTimeoutLocked(const sp<PointerController>& controller) + REQUIRES(mLock) { bool lightsOut = mLocked.systemUiVisibility & ASYSTEM_UI_VISIBILITY_STATUS_BAR_HIDDEN; controller->setInactivityTimeout(lightsOut ? PointerController::INACTIVITY_TIMEOUT_SHORT @@ -894,7 +880,7 @@ void NativeInputManager::reloadCalibration() { void NativeInputManager::setPointerIconType(int32_t iconId) { AutoMutex _l(mLock); sp<PointerController> controller = mLocked.pointerController.promote(); - if (controller != NULL) { + if (controller != nullptr) { controller->updatePointerIcon(iconId); } } @@ -902,7 +888,7 @@ void NativeInputManager::setPointerIconType(int32_t iconId) { void NativeInputManager::reloadPointerIcons() { AutoMutex _l(mLock); sp<PointerController> controller = mLocked.pointerController.promote(); - if (controller != NULL) { + if (controller != nullptr) { controller->reloadPointerResources(); } } @@ -910,7 +896,7 @@ void NativeInputManager::reloadPointerIcons() { void NativeInputManager::setCustomPointerIcon(const SpriteIcon& icon) { AutoMutex _l(mLock); sp<PointerController> controller = mLocked.pointerController.promote(); - if (controller != NULL) { + if (controller != nullptr) { controller->setCustomPointerIcon(icon); } } @@ -1122,7 +1108,7 @@ bool NativeInputManager::dispatchUnhandledKey(const sp<InputWindowHandle>& input gServiceClassInfo.dispatchUnhandledKey, inputWindowHandleObj, keyEventObj, policyFlags); if (checkAndClearExceptionFromCallback(env, "dispatchUnhandledKey")) { - fallbackKeyEventObj = NULL; + fallbackKeyEventObj = nullptr; } android_view_KeyEvent_recycle(env, keyEventObj); env->DeleteLocalRef(keyEventObj); @@ -1235,7 +1221,7 @@ int32_t NativeInputManager::getCustomPointerIconId() { static jlong nativeInit(JNIEnv* env, jclass /* clazz */, jobject serviceObj, jobject contextObj, jobject messageQueueObj) { sp<MessageQueue> messageQueue = android_os_MessageQueue_getMessageQueue(env, messageQueueObj); - if (messageQueue == NULL) { + if (messageQueue == nullptr) { jniThrowRuntimeException(env, "MessageQueue is not initialized."); return 0; } @@ -1255,37 +1241,10 @@ static void nativeStart(JNIEnv* env, jclass /* clazz */, jlong ptr) { } } -static void nativeSetVirtualDisplayViewports(JNIEnv* env, jclass /* clazz */, jlong ptr, +static void nativeSetDisplayViewports(JNIEnv* env, jclass /* clazz */, jlong ptr, jobjectArray viewportObjArray) { NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr); - im->setVirtualDisplayViewports(env, viewportObjArray); -} - -static void nativeSetDisplayViewport(JNIEnv* env, jclass /* clazz */, jlong ptr, - jint viewportType, jint displayId, jint orientation, - jint logicalLeft, jint logicalTop, jint logicalRight, jint logicalBottom, - jint physicalLeft, jint physicalTop, jint physicalRight, jint physicalBottom, - jint deviceWidth, jint deviceHeight, jstring uniqueId) { - NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr); - - DisplayViewport v; - v.displayId = displayId; - v.orientation = orientation; - v.logicalLeft = logicalLeft; - v.logicalTop = logicalTop; - v.logicalRight = logicalRight; - v.logicalBottom = logicalBottom; - v.physicalLeft = physicalLeft; - v.physicalTop = physicalTop; - v.physicalRight = physicalRight; - v.physicalBottom = physicalBottom; - v.deviceWidth = deviceWidth; - v.deviceHeight = deviceHeight; - if (uniqueId != nullptr) { - v.uniqueId = ScopedUtfChars(env, uniqueId).c_str(); - } - - im->setDisplayViewport(viewportType, v); + im->setDisplayViewports(env, viewportObjArray); } static jint nativeGetScanCodeState(JNIEnv* /* env */, jclass /* clazz */, @@ -1316,8 +1275,8 @@ static jboolean nativeHasKeys(JNIEnv* env, jclass /* clazz */, jlong ptr, jint deviceId, jint sourceMask, jintArray keyCodes, jbooleanArray outFlags) { NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr); - int32_t* codes = env->GetIntArrayElements(keyCodes, NULL); - uint8_t* flags = env->GetBooleanArrayElements(outFlags, NULL); + int32_t* codes = env->GetIntArrayElements(keyCodes, nullptr); + uint8_t* flags = env->GetBooleanArrayElements(outFlags, nullptr); jsize numCodes = env->GetArrayLength(keyCodes); jboolean result; if (numCodes == env->GetArrayLength(keyCodes)) { @@ -1356,7 +1315,7 @@ static void nativeRegisterInputChannel(JNIEnv* env, jclass /* clazz */, sp<InputChannel> inputChannel = android_view_InputChannel_getInputChannel(env, inputChannelObj); - if (inputChannel == NULL) { + if (inputChannel == nullptr) { throwInputChannelNotInitialized(env); return; } @@ -1385,12 +1344,12 @@ static void nativeUnregisterInputChannel(JNIEnv* env, jclass /* clazz */, sp<InputChannel> inputChannel = android_view_InputChannel_getInputChannel(env, inputChannelObj); - if (inputChannel == NULL) { + if (inputChannel == nullptr) { throwInputChannelNotInitialized(env); return; } - android_view_InputChannel_setDisposeCallback(env, inputChannelObj, NULL, NULL); + android_view_InputChannel_setDisposeCallback(env, inputChannelObj, nullptr, nullptr); status_t status = im->unregisterInputChannel(env, inputChannel); if (status && status != BAD_VALUE) { // ignore already unregistered channel @@ -1490,7 +1449,7 @@ static jboolean nativeTransferTouchFocus(JNIEnv* env, sp<InputChannel> toChannel = android_view_InputChannel_getInputChannel(env, toChannelObj); - if (fromChannel == NULL || toChannel == NULL) { + if (fromChannel == nullptr || toChannel == nullptr) { return JNI_FALSE; } @@ -1543,7 +1502,7 @@ static void nativeVibrate(JNIEnv* env, } jlong* patternMillis = static_cast<jlong*>(env->GetPrimitiveArrayCritical( - patternObj, NULL)); + patternObj, nullptr)); nsecs_t pattern[patternSize]; for (size_t i = 0; i < patternSize; i++) { pattern[i] = max(jlong(0), min(patternMillis[i], @@ -1656,10 +1615,8 @@ static const JNINativeMethod gInputManagerMethods[] = { (void*) nativeInit }, { "nativeStart", "(J)V", (void*) nativeStart }, - { "nativeSetVirtualDisplayViewports", "(J[Landroid/hardware/display/DisplayViewport;)V", - (void*) nativeSetVirtualDisplayViewports }, - { "nativeSetDisplayViewport", "(JIIIIIIIIIIIIILjava/lang/String;)V", - (void*) nativeSetDisplayViewport }, + { "nativeSetDisplayViewports", "(J[Landroid/hardware/display/DisplayViewport;)V", + (void*) nativeSetDisplayViewports }, { "nativeGetScanCodeState", "(JIII)I", (void*) nativeGetScanCodeState }, { "nativeGetKeyCodeState", "(JIII)I", diff --git a/services/core/jni/com_android_server_power_PowerManagerService.cpp b/services/core/jni/com_android_server_power_PowerManagerService.cpp index b2d35d4153a0..0c9b5f4999a0 100644 --- a/services/core/jni/com_android_server_power_PowerManagerService.cpp +++ b/services/core/jni/com_android_server_power_PowerManagerService.cpp @@ -19,6 +19,7 @@ //#define LOG_NDEBUG 0 #include <android/hardware/power/1.1/IPower.h> +#include <android/system/suspend/1.0/ISystemSuspend.h> #include <nativehelper/JNIHelp.h> #include "jni.h" @@ -35,7 +36,7 @@ #include <utils/Log.h> #include <hardware/power.h> #include <hardware_legacy/power.h> -#include <suspend/autosuspend.h> +#include <hidl/ServiceManagement.h> #include "com_android_server_power_PowerManagerService.h" @@ -44,6 +45,9 @@ using android::hardware::Void; using android::hardware::power::V1_0::PowerHint; using android::hardware::power::V1_0::Feature; using android::String8; +using android::system::suspend::V1_0::ISystemSuspend; +using android::system::suspend::V1_0::IWakeLock; +using android::system::suspend::V1_0::WakeLockType; using IPowerV1_1 = android::hardware::power::V1_1::IPower; using IPowerV1_0 = android::hardware::power::V1_0::IPower; @@ -171,6 +175,46 @@ void android_server_PowerManagerService_userActivity(nsecs_t eventTime, int32_t } } +static sp<ISystemSuspend> gSuspendHal = nullptr; +static sp<IWakeLock> gSuspendBlocker = nullptr; +static std::mutex gSuspendMutex; + +// Assume SystemSuspend HAL is always alive. +// TODO: Force device to restart if SystemSuspend HAL dies. +sp<ISystemSuspend> getSuspendHal() { + static std::once_flag suspendHalFlag; + std::call_once(suspendHalFlag, [](){ + ::android::hardware::details::waitForHwService(ISystemSuspend::descriptor, "default"); + gSuspendHal = ISystemSuspend::getService(); + assert(gSuspendHal != nullptr); + }); + return gSuspendHal; +} + +void enableAutoSuspend() { + static bool enabled = false; + + std::lock_guard<std::mutex> lock(gSuspendMutex); + if (!enabled) { + sp<ISystemSuspend> suspendHal = getSuspendHal(); + suspendHal->enableAutosuspend(); + enabled = true; + } + if (gSuspendBlocker) { + gSuspendBlocker->release(); + gSuspendBlocker.clear(); + } +} + +void disableAutoSuspend() { + std::lock_guard<std::mutex> lock(gSuspendMutex); + if (!gSuspendBlocker) { + sp<ISystemSuspend> suspendHal = getSuspendHal(); + gSuspendBlocker = suspendHal->acquireWakeLock(WakeLockType::PARTIAL, + "PowerManager.SuspendLockout"); + } +} + // ---------------------------------------------------------------------------- static void nativeInit(JNIEnv* env, jobject obj) { @@ -207,13 +251,13 @@ static void nativeSetInteractive(JNIEnv* /* env */, jclass /* clazz */, jboolean static void nativeSetAutoSuspend(JNIEnv* /* env */, jclass /* clazz */, jboolean enable) { if (enable) { android::base::Timer t; - autosuspend_enable(); + enableAutoSuspend(); if (t.duration() > 100ms) { ALOGD("Excessive delay in autosuspend_enable() while turning screen off"); } } else { android::base::Timer t; - autosuspend_disable(); + disableAutoSuspend(); if (t.duration() > 100ms) { ALOGD("Excessive delay in autosuspend_disable() while turning screen on"); } diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DeviceAdminServiceController.java b/services/devicepolicy/java/com/android/server/devicepolicy/DeviceAdminServiceController.java index 0c0ce8dd5174..85ca52e17ecb 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DeviceAdminServiceController.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DeviceAdminServiceController.java @@ -18,27 +18,23 @@ package com.android.server.devicepolicy; import android.Manifest.permission; import android.annotation.NonNull; import android.annotation.Nullable; +import android.app.admin.DeviceAdminService; import android.app.admin.DevicePolicyManager; import android.app.admin.IDeviceAdminService; import android.content.ComponentName; import android.content.Context; -import android.content.Intent; -import android.content.pm.ParceledListSlice; -import android.content.pm.ResolveInfo; import android.content.pm.ServiceInfo; import android.os.Handler; import android.os.IBinder; -import android.os.RemoteException; -import android.util.Log; import android.util.Slog; import android.util.SparseArray; import com.android.internal.annotations.GuardedBy; import com.android.internal.os.BackgroundThread; import com.android.server.am.PersistentConnection; +import com.android.server.appbinding.AppBindingUtils; import java.io.PrintWriter; -import java.util.List; /** * Manages connections to persistent services in owner packages. @@ -70,7 +66,13 @@ public class DeviceAdminServiceController { super(TAG, mContext, mHandler, userId, componentName, mConstants.DAS_DIED_SERVICE_RECONNECT_BACKOFF_SEC, mConstants.DAS_DIED_SERVICE_RECONNECT_BACKOFF_INCREASE, - mConstants.DAS_DIED_SERVICE_RECONNECT_MAX_BACKOFF_SEC); + mConstants.DAS_DIED_SERVICE_RECONNECT_MAX_BACKOFF_SEC, + mConstants.DAS_DIED_SERVICE_STABLE_CONNECTION_THRESHOLD_SEC); + } + + @Override + protected int getBindFlags() { + return Context.BIND_FOREGROUND_SERVICE; } @Override @@ -100,40 +102,14 @@ public class DeviceAdminServiceController { */ @Nullable private ServiceInfo findService(@NonNull String packageName, int userId) { - final Intent intent = new Intent(DevicePolicyManager.ACTION_DEVICE_ADMIN_SERVICE); - intent.setPackage(packageName); - - try { - final ParceledListSlice<ResolveInfo> pls = mInjector.getIPackageManager() - .queryIntentServices(intent, null, /* flags=*/ 0, userId); - if (pls == null) { - return null; - } - final List<ResolveInfo> list = pls.getList(); - if (list.size() == 0) { - return null; - } - // Note if multiple services are found, that's an error, even if only one of them - // is exported. - if (list.size() > 1) { - Log.e(TAG, "More than one DeviceAdminService's found in package " - + packageName - + ". They'll all be ignored."); - return null; - } - final ServiceInfo si = list.get(0).serviceInfo; - - if (!permission.BIND_DEVICE_ADMIN.equals(si.permission)) { - Log.e(TAG, "DeviceAdminService " - + si.getComponentName().flattenToShortString() - + " must be protected with " + permission.BIND_DEVICE_ADMIN - + "."); - return null; - } - return si; - } catch (RemoteException e) { - } - return null; + return AppBindingUtils.findService( + packageName, + userId, + DevicePolicyManager.ACTION_DEVICE_ADMIN_SERVICE, + permission.BIND_DEVICE_ADMIN, + DeviceAdminService.class, + mInjector.getIPackageManager(), + new StringBuilder() /* ignore error message */); } /** diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyConstants.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyConstants.java index 616c669de939..71fea02c282f 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyConstants.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyConstants.java @@ -30,14 +30,17 @@ import java.util.concurrent.TimeUnit; public class DevicePolicyConstants { private static final String TAG = DevicePolicyManagerService.LOG_TAG; - private static final String DAS_DIED_SERVICE_RECONNECT_BACKOFF_SEC_KEY - = "das_died_service_reconnect_backoff_sec"; + private static final String DAS_DIED_SERVICE_RECONNECT_BACKOFF_SEC_KEY = + "das_died_service_reconnect_backoff_sec"; - private static final String DAS_DIED_SERVICE_RECONNECT_BACKOFF_INCREASE_KEY - = "das_died_service_reconnect_backoff_increase"; + private static final String DAS_DIED_SERVICE_RECONNECT_BACKOFF_INCREASE_KEY = + "das_died_service_reconnect_backoff_increase"; - private static final String DAS_DIED_SERVICE_RECONNECT_MAX_BACKOFF_SEC_KEY - = "das_died_service_reconnect_max_backoff_sec"; + private static final String DAS_DIED_SERVICE_RECONNECT_MAX_BACKOFF_SEC_KEY = + "das_died_service_reconnect_max_backoff_sec"; + + private static final String DAS_DIED_SERVICE_STABLE_CONNECTION_THRESHOLD_SEC_KEY = + "das_died_service_stable_connection_threshold_sec"; /** * The back-off before re-connecting, when a service binding died, due to the owner @@ -55,6 +58,11 @@ public class DevicePolicyConstants { */ public final long DAS_DIED_SERVICE_RECONNECT_MAX_BACKOFF_SEC; + /** + * If a connection lasts more than this duration, we reset the re-connect back-off time. + */ + public final long DAS_DIED_SERVICE_STABLE_CONNECTION_THRESHOLD_SEC; + private DevicePolicyConstants(String settings) { final KeyValueListParser parser = new KeyValueListParser(','); @@ -75,6 +83,10 @@ public class DevicePolicyConstants { long dasDiedServiceReconnectMaxBackoffSec = parser.getLong( DAS_DIED_SERVICE_RECONNECT_MAX_BACKOFF_SEC_KEY, TimeUnit.DAYS.toSeconds(1)); + long dasDiedServiceStableConnectionThresholdSec = parser.getLong( + DAS_DIED_SERVICE_STABLE_CONNECTION_THRESHOLD_SEC_KEY, + TimeUnit.MINUTES.toSeconds(2)); + // Set minimum: 5 seconds. dasDiedServiceReconnectBackoffSec = Math.max(5, dasDiedServiceReconnectBackoffSec); @@ -89,7 +101,8 @@ public class DevicePolicyConstants { DAS_DIED_SERVICE_RECONNECT_BACKOFF_SEC = dasDiedServiceReconnectBackoffSec; DAS_DIED_SERVICE_RECONNECT_BACKOFF_INCREASE = dasDiedServiceReconnectBackoffIncrease; DAS_DIED_SERVICE_RECONNECT_MAX_BACKOFF_SEC = dasDiedServiceReconnectMaxBackoffSec; - + DAS_DIED_SERVICE_STABLE_CONNECTION_THRESHOLD_SEC = + dasDiedServiceStableConnectionThresholdSec; } public static DevicePolicyConstants loadFromString(String settings) { @@ -102,14 +115,18 @@ public class DevicePolicyConstants { pw.print(prefix); pw.print(" DAS_DIED_SERVICE_RECONNECT_BACKOFF_SEC: "); - pw.println( DAS_DIED_SERVICE_RECONNECT_BACKOFF_SEC); + pw.println(DAS_DIED_SERVICE_RECONNECT_BACKOFF_SEC); pw.print(prefix); pw.print(" DAS_DIED_SERVICE_RECONNECT_BACKOFF_INCREASE: "); - pw.println( DAS_DIED_SERVICE_RECONNECT_BACKOFF_INCREASE); + pw.println(DAS_DIED_SERVICE_RECONNECT_BACKOFF_INCREASE); pw.print(prefix); pw.print(" DAS_DIED_SERVICE_RECONNECT_MAX_BACKOFF_SEC: "); - pw.println( DAS_DIED_SERVICE_RECONNECT_MAX_BACKOFF_SEC); + pw.println(DAS_DIED_SERVICE_RECONNECT_MAX_BACKOFF_SEC); + + pw.print(prefix); + pw.print(" DAS_DIED_SERVICE_STABLE_CONNECTION_THRESHOLD_SEC: "); + pw.println(DAS_DIED_SERVICE_STABLE_CONNECTION_THRESHOLD_SEC); } } diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index e76afa3c144a..eeb4ad32408c 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -10106,13 +10106,15 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { if (setting.equals(Settings.Secure.INSTALL_NON_MARKET_APPS)) { if (getTargetSdk(who.getPackageName(), callingUserId) >= Build.VERSION_CODES.O) { throw new UnsupportedOperationException(Settings.Secure.INSTALL_NON_MARKET_APPS - + " is deprecated. Please use the user restriction " - + UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES + " instead."); + + " is deprecated. Please use one of the user restrictions " + + UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES + " or " + + UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES_GLOBALLY + " instead."); } if (!mUserManager.isManagedProfile(callingUserId)) { Slog.e(LOG_TAG, "Ignoring setSecureSetting request for " + setting + ". User restriction " - + UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES + + UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES + " or " + + UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES_GLOBALLY + " should be used instead."); } else { try { diff --git a/services/tests/servicestests/src/com/android/server/am/PersistentConnectionTest.java b/services/tests/mockingservicestests/src/com/android/server/am/PersistentConnectionTest.java index 54f93a88a387..26e77eb166ca 100644 --- a/services/tests/servicestests/src/com/android/server/am/PersistentConnectionTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/am/PersistentConnectionTest.java @@ -43,6 +43,8 @@ import java.util.Collections; @SmallTest public class PersistentConnectionTest extends AndroidTestCase { + private static final String TAG = "PersistentConnectionTest"; + private static class MyConnection extends PersistentConnection<IDeviceAdminService> { public long uptimeMillis = 12345; @@ -50,9 +52,16 @@ public class PersistentConnectionTest extends AndroidTestCase { public MyConnection(String tag, Context context, Handler handler, int userId, ComponentName componentName, long rebindBackoffSeconds, - double rebindBackoffIncrease, long rebindMaxBackoffSeconds) { + double rebindBackoffIncrease, long rebindMaxBackoffSeconds, + long resetBackoffDelay) { super(tag, context, handler, userId, componentName, - rebindBackoffSeconds, rebindBackoffIncrease, rebindMaxBackoffSeconds); + rebindBackoffSeconds, rebindBackoffIncrease, rebindMaxBackoffSeconds, + resetBackoffDelay); + } + + @Override + protected int getBindFlags() { + return Context.BIND_FOREGROUND_SERVICE; } @Override @@ -108,10 +117,11 @@ public class PersistentConnectionTest extends AndroidTestCase { final ComponentName cn = ComponentName.unflattenFromString("a.b.c/def"); final Handler handler = new Handler(Looper.getMainLooper()); - final MyConnection conn = new MyConnection("tag", context, handler, userId, cn, + final MyConnection conn = new MyConnection(TAG, context, handler, userId, cn, /* rebindBackoffSeconds= */ 5, /* rebindBackoffIncrease= */ 1.5, - /* rebindMaxBackoffSeconds= */ 11); + /* rebindMaxBackoffSeconds= */ 11, + /* resetBackoffDelay= */ 999); assertFalse(conn.isBound()); assertFalse(conn.isConnected()); @@ -310,10 +320,11 @@ public class PersistentConnectionTest extends AndroidTestCase { final ComponentName cn = ComponentName.unflattenFromString("a.b.c/def"); final Handler handler = new Handler(Looper.getMainLooper()); - final MyConnection conn = new MyConnection("tag", context, handler, userId, cn, + final MyConnection conn = new MyConnection(TAG, context, handler, userId, cn, /* rebindBackoffSeconds= */ 5, /* rebindBackoffIncrease= */ 1.5, - /* rebindMaxBackoffSeconds= */ 11); + /* rebindMaxBackoffSeconds= */ 11, + /* resetBackoffDelay= */ 999); when(context.bindServiceAsUser(any(Intent.class), any(ServiceConnection.class), anyInt(), any(Handler.class), any(UserHandle.class))) @@ -351,4 +362,78 @@ public class PersistentConnectionTest extends AndroidTestCase { assertFalse(conn.isBound()); assertFalse(conn.shouldBeBoundForTest()); } + + public void testResetBackoff() { + final Context context = mock(Context.class); + final int userId = 11; + final ComponentName cn = ComponentName.unflattenFromString("a.b.c/def"); + final Handler handler = new Handler(Looper.getMainLooper()); + + final MyConnection conn = new MyConnection(TAG, context, handler, userId, cn, + /* rebindBackoffSeconds= */ 5, + /* rebindBackoffIncrease= */ 1.5, + /* rebindMaxBackoffSeconds= */ 11, + /* resetBackoffDelay= */ 20); + + when(context.bindServiceAsUser(any(Intent.class), any(ServiceConnection.class), anyInt(), + any(Handler.class), any(UserHandle.class))) + .thenReturn(true); + + // Bind. + conn.bind(); + + assertTrue(conn.isBound()); + assertTrue(conn.shouldBeBoundForTest()); + assertFalse(conn.isRebindScheduled()); + + conn.elapse(1000); + + // Then the binding is "died"... + conn.getServiceConnectionForTest().onBindingDied(cn); + + assertFalse(conn.isBound()); + assertTrue(conn.shouldBeBoundForTest()); + assertFalse(conn.isConnected()); + assertNull(conn.getServiceBinder()); + assertTrue(conn.isRebindScheduled()); + + assertEquals(7500, conn.getNextBackoffMsForTest()); + + assertEquals( + Arrays.asList(Pair.create(conn.getBindForBackoffRunnableForTest(), + conn.uptimeMillis + 5000)), + conn.scheduledRunnables); + + // 5000 ms later... + conn.elapse(5000); + + assertTrue(conn.isBound()); + assertTrue(conn.shouldBeBoundForTest()); + assertFalse(conn.isConnected()); + assertNull(conn.getServiceBinder()); + assertFalse(conn.isRebindScheduled()); + + assertEquals(7500, conn.getNextBackoffMsForTest()); + + // Connected. + conn.getServiceConnectionForTest().onServiceConnected(cn, + new IDeviceAdminService.Stub() {}); + + assertTrue(conn.isBound()); + assertTrue(conn.shouldBeBoundForTest()); + assertTrue(conn.isConnected()); + assertNotNull(conn.getServiceBinder()); + assertFalse(conn.isRebindScheduled()); + + assertEquals(7500, conn.getNextBackoffMsForTest()); + + assertEquals( + Arrays.asList(Pair.create(conn.getStableCheckRunnableForTest(), + conn.uptimeMillis + 20000)), + conn.scheduledRunnables); + + conn.elapse(20000); + + assertEquals(5000, conn.getNextBackoffMsForTest()); + } } diff --git a/services/tests/servicestests/Android.mk b/services/tests/servicestests/Android.mk index 80307eebf53b..2957267d2e5b 100644 --- a/services/tests/servicestests/Android.mk +++ b/services/tests/servicestests/Android.mk @@ -58,6 +58,7 @@ LOCAL_JNI_SHARED_LIBRARIES := \ libbacktrace \ libbase \ libbinder \ + libbinderthreadstate \ libc++ \ libcutils \ liblog \ diff --git a/services/tests/servicestests/src/com/android/server/am/MemoryStatUtilTest.java b/services/tests/servicestests/src/com/android/server/am/MemoryStatUtilTest.java index e8a824a12300..9a283febe906 100644 --- a/services/tests/servicestests/src/com/android/server/am/MemoryStatUtilTest.java +++ b/services/tests/servicestests/src/com/android/server/am/MemoryStatUtilTest.java @@ -18,6 +18,7 @@ package com.android.server.am; import static com.android.server.am.MemoryStatUtil.BYTES_IN_KILOBYTE; import static com.android.server.am.MemoryStatUtil.MemoryStat; +import static com.android.server.am.MemoryStatUtil.PAGE_SIZE; import static com.android.server.am.MemoryStatUtil.parseMemoryMaxUsageFromMemCg; import static com.android.server.am.MemoryStatUtil.parseMemoryStatFromMemcg; import static com.android.server.am.MemoryStatUtil.parseMemoryStatFromProcfs; @@ -32,6 +33,8 @@ import androidx.test.runner.AndroidJUnit4; import org.junit.Test; import org.junit.runner.RunWith; +import java.util.Collections; + @RunWith(AndroidJUnit4.class) @SmallTest public class MemoryStatUtilTest { @@ -95,7 +98,7 @@ public class MemoryStatUtilTest { "0", "2206", "1257177088", - "3", // this is rss in bytes + "3", // this is RSS (number of pages) "4294967295", "2936971264", "2936991289", @@ -173,7 +176,7 @@ public class MemoryStatUtilTest { + "nonvoluntary_ctxt_switches:\t104\n"; @Test - public void testParseMemoryStatFromMemcg_parsesCorrectValues() throws Exception { + public void testParseMemoryStatFromMemcg_parsesCorrectValues() { MemoryStat stat = parseMemoryStatFromMemcg(MEMORY_STAT_CONTENTS); assertEquals(1, stat.pgfault); assertEquals(2, stat.pgmajfault); @@ -183,7 +186,7 @@ public class MemoryStatUtilTest { } @Test - public void testParseMemoryStatFromMemcg_emptyMemoryStatContents() throws Exception { + public void testParseMemoryStatFromMemcg_emptyMemoryStatContents() { MemoryStat stat = parseMemoryStatFromMemcg(""); assertNull(stat); @@ -204,17 +207,22 @@ public class MemoryStatUtilTest { } @Test - public void testParseMemoryStatFromProcfs_parsesCorrectValues() throws Exception { + public void testParseMemoryMaxUsageFromMemCg_incorrectValue() { + assertEquals(0, parseMemoryMaxUsageFromMemCg("memory")); + } + + @Test + public void testParseMemoryStatFromProcfs_parsesCorrectValues() { MemoryStat stat = parseMemoryStatFromProcfs(PROC_STAT_CONTENTS); assertEquals(1, stat.pgfault); assertEquals(2, stat.pgmajfault); - assertEquals(3, stat.rssInBytes); + assertEquals(3 * PAGE_SIZE, stat.rssInBytes); assertEquals(0, stat.cacheInBytes); assertEquals(0, stat.swapInBytes); } @Test - public void testParseMemoryStatFromProcfs_emptyContents() throws Exception { + public void testParseMemoryStatFromProcfs_emptyContents() { MemoryStat stat = parseMemoryStatFromProcfs(""); assertNull(stat); @@ -223,6 +231,12 @@ public class MemoryStatUtilTest { } @Test + public void testParseMemoryStatFromProcfs_invalidValue() { + String contents = String.join(" ", Collections.nCopies(24, "memory")); + assertNull(parseMemoryStatFromProcfs(contents)); + } + + @Test public void testParseVmHWMFromProcfs_parsesCorrectValue() { assertEquals(137668, parseVmHWMFromProcfs(PROC_STATUS_CONTENTS) / BYTES_IN_KILOBYTE); } diff --git a/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java index ceee60c017e5..b4212808d585 100644 --- a/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java @@ -16,6 +16,8 @@ package com.android.server.display; +import static com.android.server.display.VirtualDisplayAdapter.UNIQUE_ID_PREFIX; + import android.content.Context; import android.hardware.display.BrightnessConfiguration; import android.hardware.display.Curve; @@ -25,35 +27,48 @@ import android.hardware.display.IVirtualDisplayCallback; import android.hardware.input.InputManagerInternal; import android.os.Handler; import android.os.IBinder; -import android.os.UserHandle; -import android.test.AndroidTestCase; -import android.test.suitebuilder.annotation.SmallTest; +import android.view.Display; +import android.view.DisplayInfo; import android.view.SurfaceControl; +import androidx.test.runner.AndroidJUnit4; +import androidx.test.InstrumentationRegistry; +import androidx.test.filters.SmallTest; + import com.android.server.LocalServices; import com.android.server.SystemService; import com.android.server.display.DisplayDeviceInfo; import com.android.server.display.DisplayManagerService.SyncRoot; -import com.android.server.display.VirtualDisplayAdapter.SurfaceControlDisplayFactory; import com.android.server.lights.LightsManager; import com.android.server.wm.WindowManagerInternal; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import java.util.Arrays; import java.util.List; -import static org.mockito.Matchers.any; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import static org.mockito.Mockito.mock; @SmallTest -public class DisplayManagerServiceTest extends AndroidTestCase { +@RunWith(AndroidJUnit4.class) +public class DisplayManagerServiceTest { private static final int MSG_REGISTER_DEFAULT_DISPLAY_ADAPTERS = 1; private static final long SHORT_DEFAULT_DISPLAY_TIMEOUT_MILLIS = 10; + private Context mContext; + private final DisplayManagerService.Injector mShortMockedInjector = new DisplayManagerService.Injector() { @Override @@ -86,8 +101,8 @@ public class DisplayManagerServiceTest extends AndroidTestCase { @Mock VirtualDisplayAdapter mMockVirtualDisplayAdapter; @Mock IBinder mMockDisplayToken; - @Override - protected void setUp() throws Exception { + @Before + public void setUp() throws Exception { MockitoAnnotations.initMocks(this); LocalServices.removeServiceForTest(InputManagerInternal.class); @@ -96,15 +111,12 @@ public class DisplayManagerServiceTest extends AndroidTestCase { LocalServices.addService(WindowManagerInternal.class, mMockWindowManagerInternal); LocalServices.removeServiceForTest(LightsManager.class); LocalServices.addService(LightsManager.class, mMockLightsManager); - super.setUp(); - } - @Override - protected void tearDown() throws Exception { - super.tearDown(); + mContext = InstrumentationRegistry.getInstrumentation().getTargetContext(); } - public void testCreateVirtualDisplay_sentToInputManager() throws Exception { + @Test + public void testCreateVirtualDisplay_sentToInputManager() { DisplayManagerService displayManager = new DisplayManagerService(mContext, mBasicInjector); registerDefaultDisplays(displayManager); @@ -115,7 +127,7 @@ public class DisplayManagerServiceTest extends AndroidTestCase { DisplayManagerService.BinderService bs = displayManager.new BinderService(); String uniqueId = "uniqueId --- Test"; - String uniqueIdPrefix = "virtual:" + mContext.getPackageName() + ":"; + String uniqueIdPrefix = UNIQUE_ID_PREFIX + mContext.getPackageName() + ":"; int width = 600; int height = 800; int dpi = 320; @@ -132,19 +144,113 @@ public class DisplayManagerServiceTest extends AndroidTestCase { // flush the handler displayManager.getDisplayHandler().runWithScissors(() -> {}, 0 /* now */); - ArgumentCaptor<List<DisplayViewport>> virtualViewportCaptor = - ArgumentCaptor.forClass(List.class); - verify(mMockInputManagerInternal).setDisplayViewports( - any(), any(), virtualViewportCaptor.capture()); - - assertEquals(1, virtualViewportCaptor.getValue().size()); - DisplayViewport dv = virtualViewportCaptor.getValue().get(0); - assertEquals(height, dv.deviceHeight); - assertEquals(width, dv.deviceWidth); - assertEquals(uniqueIdPrefix + uniqueId, dv.uniqueId); - assertEquals(displayId, dv.displayId); + ArgumentCaptor<List<DisplayViewport>> viewportCaptor = ArgumentCaptor.forClass(List.class); + verify(mMockInputManagerInternal).setDisplayViewports(viewportCaptor.capture()); + List<DisplayViewport> viewports = viewportCaptor.getValue(); + + // Expect to receive 3 viewports: internal, external, and virtual + assertEquals(3, viewports.size()); + + DisplayViewport virtualViewport = null; + DisplayViewport internalViewport = null; + DisplayViewport externalViewport = null; + for (int i = 0; i < viewports.size(); i++) { + DisplayViewport v = viewports.get(i); + switch (v.type) { + case DisplayViewport.VIEWPORT_INTERNAL: { + internalViewport = v; + break; + } + case DisplayViewport.VIEWPORT_EXTERNAL: { + externalViewport = v; + break; + } + case DisplayViewport.VIEWPORT_VIRTUAL: { + virtualViewport = v; + break; + } + } + } + // INTERNAL and EXTERNAL viewports get created upon access + assertNotNull(internalViewport); + assertNotNull(externalViewport); + assertNotNull(virtualViewport); + + // INTERNAL and EXTERNAL + assertTrue(internalViewport.valid); + assertTrue(externalViewport.valid); + + // VIRTUAL + assertEquals(height, virtualViewport.deviceHeight); + assertEquals(width, virtualViewport.deviceWidth); + assertEquals(uniqueIdPrefix + uniqueId, virtualViewport.uniqueId); + assertEquals(displayId, virtualViewport.displayId); + } + + @Test + public void testPhysicalViewports() { + DisplayManagerService displayManager = + new DisplayManagerService(mContext, mBasicInjector); + registerDefaultDisplays(displayManager); + displayManager.systemReady(false /* safeMode */, true /* onlyCore */); + displayManager.windowManagerAndInputReady(); + + // This is effectively the DisplayManager service published to ServiceManager. + DisplayManagerService.BinderService bs = displayManager.new BinderService(); + + when(mMockAppToken.asBinder()).thenReturn(mMockAppToken); + + final int displayIds[] = bs.getDisplayIds(); + assertEquals(1, displayIds.length); + final int displayId = displayIds[0]; + DisplayInfo info = bs.getDisplayInfo(displayId); + assertEquals(info.type, Display.TYPE_BUILT_IN); + + displayManager.performTraversalInternal(mock(SurfaceControl.Transaction.class)); + + // flush the handler + displayManager.getDisplayHandler().runWithScissors(() -> {}, 0 /* now */); + + ArgumentCaptor<List<DisplayViewport>> viewportCaptor = ArgumentCaptor.forClass(List.class); + verify(mMockInputManagerInternal).setDisplayViewports(viewportCaptor.capture()); + List<DisplayViewport> viewports = viewportCaptor.getValue(); + + // Expect to receive 2 viewports: 1 internal, 1 external + assertEquals(2, viewports.size()); + + DisplayViewport internalViewport = null; + DisplayViewport externalViewport = null; + for (int i = 0; i < viewports.size(); i++) { + DisplayViewport v = viewports.get(i); + switch (v.type) { + case DisplayViewport.VIEWPORT_INTERNAL: { + internalViewport = v; + break; + } + case DisplayViewport.VIEWPORT_EXTERNAL: { + externalViewport = v; + break; + } + default: { + fail("Unexpected viewport type: " + DisplayViewport.typeToString(v.type)); + break; + } + } + } + // INTERNAL and EXTERNAL viewports get created upon access + assertNotNull(internalViewport); + assertNotNull(externalViewport); + assertTrue(internalViewport.valid); + assertEquals(displayId, internalViewport.displayId); + + // To simplify comparison, override the type for external Viewport + // TODO (b/116850516) remove this + externalViewport.type = internalViewport.type; + assertEquals(internalViewport, externalViewport); + externalViewport.type = DisplayViewport.VIEWPORT_EXTERNAL; // undo the changes above } + @Test public void testCreateVirtualDisplayRotatesWithContent() throws Exception { DisplayManagerService displayManager = new DisplayManagerService(mContext, mBasicInjector); @@ -178,6 +284,7 @@ public class DisplayManagerServiceTest extends AndroidTestCase { /** * Tests that the virtual display is created along-side the default display. */ + @Test public void testStartVirtualDisplayWithDefaultDisplay_Succeeds() throws Exception { DisplayManagerService displayManager = new DisplayManagerService(mContext, mShortMockedInjector); @@ -188,6 +295,7 @@ public class DisplayManagerServiceTest extends AndroidTestCase { /** * Tests that we get a Runtime exception when we cannot initialize the default display. */ + @Test public void testStartVirtualDisplayWithDefDisplay_NoDefaultDisplay() throws Exception { DisplayManagerService displayManager = new DisplayManagerService(mContext, mShortMockedInjector); @@ -206,6 +314,7 @@ public class DisplayManagerServiceTest extends AndroidTestCase { /** * Tests that we get a Runtime exception when we cannot initialize the virtual display. */ + @Test public void testStartVirtualDisplayWithDefDisplay_NoVirtualDisplayAdapter() throws Exception { DisplayManagerService displayManager = new DisplayManagerService(mContext, new DisplayManagerService.Injector() { @@ -232,6 +341,7 @@ public class DisplayManagerServiceTest extends AndroidTestCase { /** * Tests that an exception is raised for too dark a brightness configuration. */ + @Test public void testTooDarkBrightnessConfigurationThrowException() { DisplayManagerService displayManager = new DisplayManagerService(mContext, mShortMockedInjector); @@ -266,6 +376,7 @@ public class DisplayManagerServiceTest extends AndroidTestCase { /** * Tests that no exception is raised for not too dark a brightness configuration. */ + @Test public void testBrightEnoughBrightnessConfigurationDoesNotThrowException() { DisplayManagerService displayManager = new DisplayManagerService(mContext, mShortMockedInjector); @@ -279,6 +390,7 @@ public class DisplayManagerServiceTest extends AndroidTestCase { /** * Tests that null brightness configurations are alright. */ + @Test public void testNullBrightnessConfiguration() { DisplayManagerService displayManager = new DisplayManagerService(mContext, mShortMockedInjector); diff --git a/services/tests/servicestests/src/com/android/server/usage/UsageStatsDatabaseTest.java b/services/tests/servicestests/src/com/android/server/usage/UsageStatsDatabaseTest.java new file mode 100644 index 000000000000..5a787ec1cff3 --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/usage/UsageStatsDatabaseTest.java @@ -0,0 +1,352 @@ +/* + * 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.usage; + +import static junit.framework.TestCase.assertNull; +import static junit.framework.TestCase.fail; + +import static org.testng.Assert.assertEquals; + +import android.app.usage.EventList; +import android.app.usage.UsageEvents; +import android.app.usage.UsageStats; +import android.app.usage.UsageStatsManager; +import android.content.Context; +import android.content.res.Configuration; +import android.test.suitebuilder.annotation.SmallTest; + +import androidx.test.InstrumentationRegistry; +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.io.File; +import java.io.IOException; +import java.util.List; +import java.util.Locale; + +@RunWith(AndroidJUnit4.class) +@SmallTest +public class UsageStatsDatabaseTest { + protected Context mContext; + private UsageStatsDatabase mUsageStatsDatabase; + private File mTestDir; + + private IntervalStats mIntervalStats = new IntervalStats(); + private long mEndTime = 0; + + private static final UsageStatsDatabase.StatCombiner<IntervalStats> mIntervalStatsVerifier = + new UsageStatsDatabase.StatCombiner<IntervalStats>() { + @Override + public void combine(IntervalStats stats, boolean mutable, + List<IntervalStats> accResult) { + accResult.add(stats); + } + }; + + @Before + public void setUp() { + mContext = InstrumentationRegistry.getTargetContext(); + mTestDir = new File(mContext.getFilesDir(), "UsageStatsDatabaseTest"); + mUsageStatsDatabase = new UsageStatsDatabase(mTestDir); + mUsageStatsDatabase.init(1); + populateIntervalStats(); + clearUsageStatsFiles(); + } + + /** + * A debugging utility for viewing the files currently in the test directory + */ + private void clearUsageStatsFiles() { + File[] intervalDirs = mTestDir.listFiles(); + for (File intervalDir : intervalDirs) { + if (intervalDir.isDirectory()) { + File[] usageFiles = intervalDir.listFiles(); + for (File f : usageFiles) { + f.delete(); + } + } + } + } + + /** + * A debugging utility for viewing the files currently in the test directory + */ + private String dumpUsageStatsFiles() { + StringBuilder sb = new StringBuilder(); + File[] intervalDirs = mTestDir.listFiles(); + for (File intervalDir : intervalDirs) { + if (intervalDir.isDirectory()) { + File[] usageFiles = intervalDir.listFiles(); + for (File f : usageFiles) { + sb.append(f.toString()); + } + } + } + return sb.toString(); + } + + private void populateIntervalStats() { + final int numberOfEvents = 3000; + long time = 1; + mIntervalStats = new IntervalStats(); + + mIntervalStats.beginTime = 1; + mIntervalStats.interactiveTracker.count = 2; + mIntervalStats.interactiveTracker.duration = 111111; + mIntervalStats.nonInteractiveTracker.count = 3; + mIntervalStats.nonInteractiveTracker.duration = 222222; + mIntervalStats.keyguardShownTracker.count = 4; + mIntervalStats.keyguardShownTracker.duration = 333333; + mIntervalStats.keyguardHiddenTracker.count = 5; + mIntervalStats.keyguardHiddenTracker.duration = 4444444; + + if (mIntervalStats.events == null) { + mIntervalStats.events = new EventList(); + } + + for (int i = 0; i < numberOfEvents; i++) { + UsageEvents.Event event = new UsageEvents.Event(); + final int packageInt = ((i / 3) % 7); + event.mPackage = "fake.package.name" + packageInt; //clusters of 3 events from 7 "apps" + if (packageInt == 3) { + // Third app is an instant app + event.mFlags |= UsageEvents.Event.FLAG_IS_PACKAGE_INSTANT_APP; + } else if (packageInt == 2 || packageInt == 4) { + event.mClass = ".fake.class.name" + i % 11; + } + + + event.mTimeStamp = time; + event.mEventType = i % 19; //"random" event type + + switch (event.mEventType) { + case UsageEvents.Event.CONFIGURATION_CHANGE: + //empty config, + event.mConfiguration = new Configuration(); + break; + case UsageEvents.Event.SHORTCUT_INVOCATION: + //"random" shortcut + event.mShortcutId = "shortcut" + (i % 8); + break; + case UsageEvents.Event.STANDBY_BUCKET_CHANGED: + //"random" bucket and reason + event.mBucketAndReason = (((i % 5 + 1) * 10) << 16) & (i % 5 + 1) << 8; + break; + case UsageEvents.Event.NOTIFICATION_INTERRUPTION: + //"random" channel + event.mNotificationChannelId = "channel" + (i % 5); + break; + } + + mIntervalStats.events.insert(event); + mIntervalStats.update(event.mPackage, event.mTimeStamp, event.mEventType); + + time += 23; // Arbitrary progression of time + } + mEndTime = time; + + Configuration config1 = new Configuration(); + config1.fontScale = 3.3f; + config1.mcc = 4; + mIntervalStats.getOrCreateConfigurationStats(config1); + + Configuration config2 = new Configuration(); + config2.mnc = 5; + config2.setLocale(new Locale("en", "US")); + mIntervalStats.getOrCreateConfigurationStats(config2); + + Configuration config3 = new Configuration(); + config3.touchscreen = 6; + config3.keyboard = 7; + mIntervalStats.getOrCreateConfigurationStats(config3); + + Configuration config4 = new Configuration(); + config4.keyboardHidden = 8; + config4.hardKeyboardHidden = 9; + mIntervalStats.getOrCreateConfigurationStats(config4); + + Configuration config5 = new Configuration(); + config5.navigation = 10; + config5.navigationHidden = 11; + mIntervalStats.getOrCreateConfigurationStats(config5); + + Configuration config6 = new Configuration(); + config6.orientation = 12; + //Ignore screen layout, it's determined by locale + mIntervalStats.getOrCreateConfigurationStats(config6); + + Configuration config7 = new Configuration(); + config7.colorMode = 14; + config7.uiMode = 15; + mIntervalStats.getOrCreateConfigurationStats(config7); + + Configuration config8 = new Configuration(); + config8.screenWidthDp = 16; + config8.screenHeightDp = 17; + mIntervalStats.getOrCreateConfigurationStats(config8); + + Configuration config9 = new Configuration(); + config9.smallestScreenWidthDp = 18; + config9.densityDpi = 19; + mIntervalStats.getOrCreateConfigurationStats(config9); + + mIntervalStats.activeConfiguration = config9; + } + + void compareUsageStats(UsageStats us1, UsageStats us2) { + assertEquals(us1.mPackageName, us2.mPackageName); + // mBeginTimeStamp is based on the enclosing IntervalStats, don't bother checking + // mEndTimeStamp is based on the enclosing IntervalStats, don't bother checking + assertEquals(us1.mLastTimeUsed, us2.mLastTimeUsed); + assertEquals(us1.mTotalTimeInForeground, us2.mTotalTimeInForeground); + // mLaunchCount not persisted, so skipped + assertEquals(us1.mAppLaunchCount, us2.mAppLaunchCount); + assertEquals(us1.mLastEvent, us2.mLastEvent); + assertEquals(us1.mChooserCounts, us2.mChooserCounts); + } + + void compareUsageEvent(UsageEvents.Event e1, UsageEvents.Event e2, int debugId) { + assertEquals(e1.mPackage, e2.mPackage, "Usage event " + debugId); + assertEquals(e1.mClass, e2.mClass, "Usage event " + debugId); + assertEquals(e1.mTimeStamp, e2.mTimeStamp, "Usage event " + debugId); + assertEquals(e1.mEventType, e2.mEventType, "Usage event " + debugId); + switch (e1.mEventType) { + case UsageEvents.Event.CONFIGURATION_CHANGE: + assertEquals(e1.mConfiguration, e2.mConfiguration, + "Usage event " + debugId + e2.mConfiguration.toString()); + break; + case UsageEvents.Event.SHORTCUT_INVOCATION: + assertEquals(e1.mShortcutId, e2.mShortcutId, "Usage event " + debugId); + break; + case UsageEvents.Event.STANDBY_BUCKET_CHANGED: + assertEquals(e1.mBucketAndReason, e2.mBucketAndReason, "Usage event " + debugId); + break; + case UsageEvents.Event.NOTIFICATION_INTERRUPTION: + assertEquals(e1.mNotificationChannelId, e2.mNotificationChannelId, + "Usage event " + debugId); + break; + } + assertEquals(e1.mFlags, e2.mFlags); + } + + void compareIntervalStats(IntervalStats stats1, IntervalStats stats2) { + assertEquals(stats1.beginTime, stats2.beginTime); + assertEquals(stats1.endTime, stats2.endTime); + assertEquals(stats1.interactiveTracker.count, stats2.interactiveTracker.count); + assertEquals(stats1.interactiveTracker.duration, stats2.interactiveTracker.duration); + assertEquals(stats1.nonInteractiveTracker.count, stats2.nonInteractiveTracker.count); + assertEquals(stats1.nonInteractiveTracker.duration, stats2.nonInteractiveTracker.duration); + assertEquals(stats1.keyguardShownTracker.count, stats2.keyguardShownTracker.count); + assertEquals(stats1.keyguardShownTracker.duration, stats2.keyguardShownTracker.duration); + assertEquals(stats1.keyguardHiddenTracker.count, stats2.keyguardHiddenTracker.count); + assertEquals(stats1.keyguardHiddenTracker.duration, stats2.keyguardHiddenTracker.duration); + + String[] usageKey1 = stats1.packageStats.keySet().toArray(new String[0]); + String[] usageKey2 = stats2.packageStats.keySet().toArray(new String[0]); + for (int i = 0; i < usageKey1.length; i++) { + UsageStats usageStats1 = stats1.packageStats.get(usageKey1[i]); + UsageStats usageStats2 = stats2.packageStats.get(usageKey2[i]); + compareUsageStats(usageStats1, usageStats2); + } + + assertEquals(stats1.configurations.size(), stats2.configurations.size()); + Configuration[] configSet1 = stats1.configurations.keySet().toArray(new Configuration[0]); + for (int i = 0; i < configSet1.length; i++) { + if (!stats2.configurations.containsKey(configSet1[i])) { + Configuration[] configSet2 = stats2.configurations.keySet().toArray( + new Configuration[0]); + String debugInfo = ""; + for (Configuration c : configSet1) { + debugInfo += c.toString() + "\n"; + } + debugInfo += "\n"; + for (Configuration c : configSet2) { + debugInfo += c.toString() + "\n"; + } + fail("Config " + configSet1[i].toString() + + " not found in deserialized IntervalStat\n" + debugInfo); + } + } + assertEquals(stats1.activeConfiguration, stats2.activeConfiguration); + + assertEquals(stats1.events.size(), stats2.events.size()); + for (int i = 0; i < stats1.events.size(); i++) { + compareUsageEvent(stats1.events.get(i), stats2.events.get(i), i); + } + } + + /** + * Runs the Write Read test. + * Will write the generated IntervalStat to disk, read it from disk and compare the two + */ + void runWriteReadTest(int interval) throws IOException { + mUsageStatsDatabase.putUsageStats(interval, mIntervalStats); + List<IntervalStats> stats = mUsageStatsDatabase.queryUsageStats(interval, 0, mEndTime, + mIntervalStatsVerifier); + + assertEquals(1, stats.size()); + compareIntervalStats(mIntervalStats, stats.get(0)); + } + + /** + * Demonstrate that IntervalStats can be serialized and deserialized from disk without loss of + * relevant data. + */ + @Test + public void testWriteRead() throws IOException { + runWriteReadTest(UsageStatsManager.INTERVAL_DAILY); + runWriteReadTest(UsageStatsManager.INTERVAL_WEEKLY); + runWriteReadTest(UsageStatsManager.INTERVAL_MONTHLY); + runWriteReadTest(UsageStatsManager.INTERVAL_YEARLY); + } + + /** + * Runs the Version Change tests. + * Will write the generated IntervalStat to disk in one version format, "upgrade" to another + * version and read the automatically upgraded files on disk in the new file format. + */ + void runVersionChangeTest(int oldVersion, int newVersion, int interval) throws IOException { + // Write IntervalStats to disk in old version format + UsageStatsDatabase prevDB = new UsageStatsDatabase(mTestDir, oldVersion); + prevDB.init(1); + prevDB.putUsageStats(interval, mIntervalStats); + + // Simulate an upgrade to a new version and read from the disk + UsageStatsDatabase newDB = new UsageStatsDatabase(mTestDir, newVersion); + newDB.init(mEndTime); + List<IntervalStats> stats = newDB.queryUsageStats(interval, 0, mEndTime, + mIntervalStatsVerifier); + + assertEquals(1, stats.size()); + // The written and read IntervalStats should match + compareIntervalStats(mIntervalStats, stats.get(0)); + } + + /** + * Test the version upgrade from 3 to 4 + */ + @Test + public void testVersionUpgradeFrom3to4() throws IOException { + runVersionChangeTest(3, 4, UsageStatsManager.INTERVAL_DAILY); + runVersionChangeTest(3, 4, UsageStatsManager.INTERVAL_WEEKLY); + runVersionChangeTest(3, 4, UsageStatsManager.INTERVAL_MONTHLY); + runVersionChangeTest(3, 4, UsageStatsManager.INTERVAL_YEARLY); + } +} diff --git a/services/tests/servicestests/src/com/android/server/wm/RecentsAnimationControllerTest.java b/services/tests/servicestests/src/com/android/server/wm/RecentsAnimationControllerTest.java index aaa00452204b..088e22972cc9 100644 --- a/services/tests/servicestests/src/com/android/server/wm/RecentsAnimationControllerTest.java +++ b/services/tests/servicestests/src/com/android/server/wm/RecentsAnimationControllerTest.java @@ -40,6 +40,7 @@ import android.util.SparseBooleanArray; import android.view.IRecentsAnimationRunner; import android.view.SurfaceControl; +import androidx.test.filters.FlakyTest; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; @@ -114,6 +115,7 @@ public class RecentsAnimationControllerTest extends WindowTestsBase { } @Test + @FlakyTest(bugId = 117117823) public void testIncludedApps_expectTargetAndVisible() throws Exception { sWm.setRecentsAnimationController(mController); final AppWindowToken homeAppWindow = createAppWindowToken(mDisplayContent, diff --git a/services/tests/uiservicestests/Android.mk b/services/tests/uiservicestests/Android.mk index 840517954a0a..f3f43558115b 100644 --- a/services/tests/uiservicestests/Android.mk +++ b/services/tests/uiservicestests/Android.mk @@ -45,6 +45,7 @@ LOCAL_JNI_SHARED_LIBRARIES := \ libbacktrace \ libbase \ libbinder \ + libbinderthreadstate \ libc++ \ libcutils \ liblog \ diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java index 45d2fa2d16a4..58aae2b12c21 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java @@ -2192,6 +2192,26 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { } @Test + public void testDontAutogroupIfCritical() throws Exception { + NotificationRecord r = generateNotificationRecord(mTestNotificationChannel, 0, null, false); + r.setCriticality(CriticalNotificationExtractor.CRITICAL_LOW); + mService.addEnqueuedNotification(r); + NotificationManagerService.PostNotificationRunnable runnable = + mService.new PostNotificationRunnable(r.getKey()); + runnable.run(); + + r = generateNotificationRecord(mTestNotificationChannel, 1, null, false); + r.setCriticality(CriticalNotificationExtractor.CRITICAL); + runnable = mService.new PostNotificationRunnable(r.getKey()); + mService.addEnqueuedNotification(r); + + runnable.run(); + waitForIdle(); + + verify(mGroupHelper, never()).onNotificationPosted(any(), anyBoolean()); + } + + @Test public void testNoFakeColorizedPermission() throws Exception { when(mPackageManagerClient.checkPermission(any(), any())).thenReturn(PERMISSION_DENIED); Notification.Builder nb = new Notification.Builder(mContext, @@ -3428,17 +3448,14 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { } @Test - public void testResolveNotificationUid_sameAppWrongPkg() throws Exception { + public void testResolveNotificationUid_sameAppDiffPackage() throws Exception { ApplicationInfo info = new ApplicationInfo(); info.uid = Binder.getCallingUid(); - when(mPackageManager.getApplicationInfo(anyString(), anyInt(), anyInt())).thenReturn(info); + when(mPackageManager.getApplicationInfo(anyString(), anyInt(), eq(0))).thenReturn(info); - try { - mService.resolveNotificationUid("caller", "other", info.uid, 0); - fail("Incorrect pkg didn't throw security exception"); - } catch (SecurityException e) { - // yay - } + int actualUid = mService.resolveNotificationUid("caller", "callerAlso", info.uid, 0); + + assertEquals(info.uid, actualUid); } @Test diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java index 702161e48b75..13f3e5e7a3ad 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java @@ -1030,6 +1030,14 @@ public class ZenModeHelperTest extends UiServiceTestCase { assertEquals(1, mZenModeHelperSpy.mConditions.mSubscriptions.size()); } + @Test + public void testEmptyDefaultRulesMap() { + ZenModeConfig config = new ZenModeConfig(); + config.automaticRules = new ArrayMap<>(); + mZenModeHelperSpy.mConfig = config; + mZenModeHelperSpy.updateDefaultZenRules(); // shouldn't throw null pointer + } + private void setupZenConfig() { mZenModeHelperSpy.mZenMode = Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS; mZenModeHelperSpy.mConfig.allowAlarms = false; diff --git a/services/usage/java/com/android/server/usage/IntervalStats.java b/services/usage/java/com/android/server/usage/IntervalStats.java index 4b7e21f2d536..db9972f96b21 100644 --- a/services/usage/java/com/android/server/usage/IntervalStats.java +++ b/services/usage/java/com/android/server/usage/IntervalStats.java @@ -24,7 +24,9 @@ import android.app.usage.UsageStats; import android.content.res.Configuration; import android.util.ArrayMap; import android.util.ArraySet; +import android.util.proto.ProtoInputStream; +import java.io.IOException; import java.util.List; import com.android.internal.annotations.VisibleForTesting; @@ -46,7 +48,7 @@ public class IntervalStats { // keep hundreds of strings that have the same contents. We will read the string // and only keep it if it's not in the cache. The GC will take care of the // strings that had identical copies in the cache. - private final ArraySet<String> mStringCache = new ArraySet<>(); + public final ArraySet<String> mStringCache = new ArraySet<>(); public static final class EventTracker { public long curStartTime; @@ -129,6 +131,90 @@ public class IntervalStats { return event; } + /** + * Builds a UsageEvents.Event from a proto, but does not add it internally. + * Built here to take advantage of the cached String Refs + */ + UsageEvents.Event buildEvent(ProtoInputStream parser, List<String> stringPool) + throws IOException { + final UsageEvents.Event event = new UsageEvents.Event(); + while (true) { + switch (parser.nextField()) { + case (int) IntervalStatsProto.Event.PACKAGE: + event.mPackage = getCachedStringRef( + parser.readString(IntervalStatsProto.Event.PACKAGE)); + break; + case (int) IntervalStatsProto.Event.PACKAGE_INDEX: + event.mPackage = getCachedStringRef(stringPool.get( + parser.readInt(IntervalStatsProto.Event.PACKAGE_INDEX) - 1)); + break; + case (int) IntervalStatsProto.Event.CLASS: + event.mClass = getCachedStringRef( + parser.readString(IntervalStatsProto.Event.CLASS)); + break; + case (int) IntervalStatsProto.Event.CLASS_INDEX: + event.mClass = getCachedStringRef(stringPool.get( + parser.readInt(IntervalStatsProto.Event.CLASS_INDEX) - 1)); + break; + case (int) IntervalStatsProto.Event.TIME_MS: + event.mTimeStamp = beginTime + parser.readLong( + IntervalStatsProto.Event.TIME_MS); + break; + case (int) IntervalStatsProto.Event.FLAGS: + event.mFlags = parser.readInt(IntervalStatsProto.Event.FLAGS); + break; + case (int) IntervalStatsProto.Event.TYPE: + event.mEventType = parser.readInt(IntervalStatsProto.Event.TYPE); + break; + case (int) IntervalStatsProto.Event.CONFIG: + event.mConfiguration = new Configuration(); + event.mConfiguration.readFromProto(parser, IntervalStatsProto.Event.CONFIG); + break; + case (int) IntervalStatsProto.Event.SHORTCUT_ID: + event.mShortcutId = parser.readString( + IntervalStatsProto.Event.SHORTCUT_ID).intern(); + break; + case (int) IntervalStatsProto.Event.STANDBY_BUCKET: + event.mBucketAndReason = parser.readInt( + IntervalStatsProto.Event.STANDBY_BUCKET); + break; + case (int) IntervalStatsProto.Event.NOTIFICATION_CHANNEL: + event.mNotificationChannelId = parser.readString( + IntervalStatsProto.Event.NOTIFICATION_CHANNEL); + break; + case (int) IntervalStatsProto.Event.NOTIFICATION_CHANNEL_INDEX: + event.mNotificationChannelId = getCachedStringRef(stringPool.get( + parser.readInt(IntervalStatsProto.Event.NOTIFICATION_CHANNEL_INDEX) + - 1)); + break; + case ProtoInputStream.NO_MORE_FIELDS: + // Handle default values for certain events types + switch (event.mEventType) { + case UsageEvents.Event.CONFIGURATION_CHANGE: + if (event.mConfiguration == null) { + event.mConfiguration = new Configuration(); + } + break; + case UsageEvents.Event.SHORTCUT_INVOCATION: + if (event.mShortcutId == null) { + event.mShortcutId = ""; + } + break; + case UsageEvents.Event.NOTIFICATION_INTERRUPTION: + if (event.mNotificationChannelId == null) { + event.mNotificationChannelId = ""; + } + break; + } + if (event.mTimeStamp == 0) { + //mTimestamp not set, assume default value 0 plus beginTime + event.mTimeStamp = beginTime; + } + return event; + } + } + } + private boolean isStatefulEvent(int eventType) { switch (eventType) { case UsageEvents.Event.MOVE_TO_FOREGROUND: @@ -143,8 +229,6 @@ public class IntervalStats { /** * Returns whether the event type is one caused by user visible * interaction. Excludes those that are internally generated. - * @param eventType - * @return */ private boolean isUserVisibleEvent(int eventType) { return eventType != UsageEvents.Event.SYSTEM_INTERACTION @@ -184,6 +268,25 @@ public class IntervalStats { endTime = timeStamp; } + /** + * @hide + */ + @VisibleForTesting + public void addEvent(UsageEvents.Event event) { + if (events == null) { + events = new EventList(); + } + // Cache common use strings + event.mPackage = getCachedStringRef(event.mPackage); + if (event.mClass != null) { + event.mClass = getCachedStringRef(event.mClass); + } + if (event.mEventType == UsageEvents.Event.NOTIFICATION_INTERRUPTION) { + event.mNotificationChannelId = getCachedStringRef(event.mNotificationChannelId); + } + events.insert(event); + } + void updateChooserCounts(String packageName, String category, String action) { UsageStats usageStats = getOrCreateUsageStats(packageName); if (usageStats.mChooserCounts == null) { diff --git a/services/usage/java/com/android/server/usage/UsageStatsDatabase.java b/services/usage/java/com/android/server/usage/UsageStatsDatabase.java index 5ab5dc223d9e..c616685ba302 100644 --- a/services/usage/java/com/android/server/usage/UsageStatsDatabase.java +++ b/services/usage/java/com/android/server/usage/UsageStatsDatabase.java @@ -17,6 +17,7 @@ package com.android.server.usage; import android.app.usage.TimeSparseArray; +import android.app.usage.UsageEvents; import android.app.usage.UsageStats; import android.app.usage.UsageStatsManager; import android.os.Build; @@ -25,6 +26,10 @@ import android.util.AtomicFile; import android.util.Slog; import android.util.TimeUtils; +import com.android.internal.annotations.VisibleForTesting; + +import libcore.io.IoUtils; + import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.ByteArrayInputStream; @@ -32,18 +37,49 @@ import java.io.ByteArrayOutputStream; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; import java.io.FileReader; import java.io.FileWriter; import java.io.FilenameFilter; +import java.io.InputStream; import java.io.IOException; +import java.io.OutputStream; +import java.nio.file.Files; +import java.nio.file.StandardCopyOption; import java.util.ArrayList; import java.util.List; /** - * Provides an interface to query for UsageStat data from an XML database. + * Provides an interface to query for UsageStat data from a Protocol Buffer database. + * + * Prior to version 4, UsageStatsDatabase used XML to store Usage Stats data to disk. + * When the UsageStatsDatabase version is upgraded, the files on disk are migrated to the new + * version on init. The steps of migration are as follows: + * 1) Check if version upgrade breadcrumb exists on disk, if so skip to step 4. + * 2) Copy current files to versioned backup files. + * 3) Write a temporary breadcrumb file with some info about the backed up files. + * 4) Deserialize a versioned backup file using the info written to the breadcrumb for the + * correct deserialization methodology. + * 5) Reserialize the data read from the file with the new version format and replace the old files + * 6) Repeat Step 3 and 4 for each versioned backup file matching the breadcrumb file. + * 7) Update the version file with the new version and build fingerprint. + * 8) Delete the versioned backup files (unless flagged to be kept). + * 9) Delete the breadcrumb file. + * + * Performing the upgrade steps in this order, protects against unexpected shutdowns mid upgrade + * + * A versioned backup file is simply a copy of a Usage Stats file with some extra info embedded in + * the file name. The structure of the versioned backup filename is as followed: + * (original file name).(backup timestamp).(original file version).vak + * + * During the version upgrade process, the new upgraded file will have it's name set to the original + * file name. The backup timestamp helps distinguish between versioned backups if multiple upgrades + * and downgrades have taken place. The original file version denotes how to parse the file. */ public class UsageStatsDatabase { - private static final int CURRENT_VERSION = 3; + private static final int DEFAULT_CURRENT_VERSION = 4; // Current version of the backup schema static final int BACKUP_VERSION = 1; @@ -52,10 +88,16 @@ public class UsageStatsDatabase { // same as UsageStatsBackupHelper.KEY_USAGE_STATS static final String KEY_USAGE_STATS = "usage_stats"; + // Persist versioned backup files. + // Should be false, except when testing new versions + // STOPSHIP: b/111422946 this should be false on launch + static final boolean KEEP_VAK_FILES = true; private static final String TAG = "UsageStatsDatabase"; - private static final boolean DEBUG = UsageStatsService.DEBUG; + // STOPSHIP: b/111422946 this should be boolean DEBUG = UsageStatsService.DEBUG; on launch + private static final boolean DEBUG = true; private static final String BAK_SUFFIX = ".bak"; + private static final String VERSIONED_BAK_SUFFIX = ".vak"; private static final String CHECKED_IN_SUFFIX = UsageStatsXml.CHECKED_IN_SUFFIX; private static final String RETENTION_LEN_KEY = "ro.usagestats.chooser.retention"; private static final int SELECTION_LOG_RETENTION_LEN = @@ -66,21 +108,40 @@ public class UsageStatsDatabase { private final TimeSparseArray<AtomicFile>[] mSortedStatFiles; private final UnixCalendar mCal; private final File mVersionFile; + // If this file exists on disk, UsageStatsDatabase is in the middle of migrating files to a new + // version. If this file exists on boot, the upgrade was interrupted and needs to be picked up + // where it left off. + private final File mUpdateBreadcrumb; + // Current version of the database files schema + private final int mCurrentVersion; private boolean mFirstUpdate; private boolean mNewUpdate; - public UsageStatsDatabase(File dir) { - mIntervalDirs = new File[] { + /** + * UsageStatsDatabase constructor that allows setting the version number. + * This should only be used for testing. + * + * @hide + */ + @VisibleForTesting + public UsageStatsDatabase(File dir, int version) { + mIntervalDirs = new File[]{ new File(dir, "daily"), new File(dir, "weekly"), new File(dir, "monthly"), new File(dir, "yearly"), }; + mCurrentVersion = version; mVersionFile = new File(dir, "version"); + mUpdateBreadcrumb = new File(dir, "breadcrumb"); mSortedStatFiles = new TimeSparseArray[mIntervalDirs.length]; mCal = new UnixCalendar(0); } + public UsageStatsDatabase(File dir) { + this(dir, DEFAULT_CURRENT_VERSION); + } + /** * Initialize any directories required and index what stats are available. */ @@ -154,7 +215,7 @@ public class UsageStatsDatabase { try { IntervalStats stats = new IntervalStats(); for (int i = start; i < fileCount - 1; i++) { - UsageStatsXml.read(files.valueAt(i), stats); + readLocked(files.valueAt(i), stats); if (!checkinAction.checkin(stats)) { return false; } @@ -190,7 +251,7 @@ public class UsageStatsDatabase { final FilenameFilter backupFileFilter = new FilenameFilter() { @Override public boolean accept(File dir, String name) { - return !name.endsWith(BAK_SUFFIX); + return !name.endsWith(BAK_SUFFIX) && !name.endsWith(VERSIONED_BAK_SUFFIX); } }; @@ -210,7 +271,7 @@ public class UsageStatsDatabase { for (File f : files) { final AtomicFile af = new AtomicFile(f); try { - mSortedStatFiles[i].put(UsageStatsXml.parseBeginTime(af), af); + mSortedStatFiles[i].put(parseBeginTime(af), af); } catch (IOException e) { Slog.e(TAG, "failed to index file: " + f, e); } @@ -252,14 +313,32 @@ public class UsageStatsDatabase { version = 0; } - if (version != CURRENT_VERSION) { - Slog.i(TAG, "Upgrading from version " + version + " to " + CURRENT_VERSION); - doUpgradeLocked(version); + if (version != mCurrentVersion) { + Slog.i(TAG, "Upgrading from version " + version + " to " + mCurrentVersion); + if (!mUpdateBreadcrumb.exists()) { + doUpgradeLocked(version); + } else { + Slog.i(TAG, "Version upgrade breadcrumb found on disk! Continuing version upgrade"); + } + + if (mUpdateBreadcrumb.exists()) { + int previousVersion; + long token; + try (BufferedReader reader = new BufferedReader( + new FileReader(mUpdateBreadcrumb))) { + token = Long.parseLong(reader.readLine()); + previousVersion = Integer.parseInt(reader.readLine()); + } catch (NumberFormatException | IOException e) { + Slog.e(TAG, "Failed read version upgrade breadcrumb"); + throw new RuntimeException(e); + } + continueUpgradeLocked(previousVersion, token); + } } - if (version != CURRENT_VERSION || mNewUpdate) { + if (version != mCurrentVersion || mNewUpdate) { try (BufferedWriter writer = new BufferedWriter(new FileWriter(mVersionFile))) { - writer.write(Integer.toString(CURRENT_VERSION)); + writer.write(Integer.toString(mCurrentVersion)); writer.write("\n"); writer.write(currentFingerprint); writer.write("\n"); @@ -269,6 +348,14 @@ public class UsageStatsDatabase { throw new RuntimeException(e); } } + + if (mUpdateBreadcrumb.exists()) { + // Files should be up to date with current version. Clear the version update breadcrumb + if (!KEEP_VAK_FILES) { + removeVersionedBackupFiles(); + } + mUpdateBreadcrumb.delete(); + } } private String getBuildFingerprint() { @@ -290,6 +377,119 @@ public class UsageStatsDatabase { } } } + } else { + // Turn all current usage stats files into versioned backup files + final long token = System.currentTimeMillis(); + final FilenameFilter backupFileFilter = new FilenameFilter() { + @Override + public boolean accept(File dir, String name) { + return !name.endsWith(BAK_SUFFIX) && !name.endsWith(VERSIONED_BAK_SUFFIX); + } + }; + + for (int i = 0; i < mIntervalDirs.length; i++) { + File[] files = mIntervalDirs[i].listFiles(backupFileFilter); + if (files != null) { + for (int j = 0; j < files.length; j++) { + final File backupFile = new File( + files[j].toString() + "." + Long.toString(token) + "." + + Integer.toString(thisVersion) + VERSIONED_BAK_SUFFIX); + if (DEBUG) { + Slog.d(TAG, "Creating versioned (" + Integer.toString(thisVersion) + + ") backup of " + files[j].toString() + + " stat files for interval " + + i + " to " + backupFile.toString()); + } + + try { + // Backup file should not already exist, but make sure it doesn't + Files.deleteIfExists(backupFile.toPath()); + Files.move(files[j].toPath(), backupFile.toPath(), + StandardCopyOption.ATOMIC_MOVE); + } catch (IOException e) { + Slog.e(TAG, "Failed to back up file : " + files[j].toString()); + throw new RuntimeException(e); + } + } + } + } + + // Leave a breadcrumb behind noting that all the usage stats have been copied to a + // versioned backup. + BufferedWriter writer = null; + try { + writer = new BufferedWriter(new FileWriter(mUpdateBreadcrumb)); + writer.write(Long.toString(token)); + writer.write("\n"); + writer.write(Integer.toString(thisVersion)); + writer.write("\n"); + writer.flush(); + } catch (IOException e) { + Slog.e(TAG, "Failed to write new version upgrade breadcrumb"); + throw new RuntimeException(e); + } finally { + IoUtils.closeQuietly(writer); + } + } + } + + private void continueUpgradeLocked(int version, long token) { + // Read all the backed ups for the specified version and rewrite them with the current + // version's file format. + final FilenameFilter versionedBackupFileFilter = new FilenameFilter() { + @Override + public boolean accept(File dir, String name) { + return name.endsWith("." + Long.toString(token) + "." + Integer.toString(version) + + VERSIONED_BAK_SUFFIX); + } + }; + + for (int i = 0; i < mIntervalDirs.length; i++) { + File[] files = mIntervalDirs[i].listFiles(versionedBackupFileFilter); + if (files != null) { + for (int j = 0; j < files.length; j++) { + if (DEBUG) { + Slog.d(TAG, + "Upgrading " + files[j].toString() + " to version (" + + Integer.toString( + mCurrentVersion) + ") for interval " + i); + } + try { + IntervalStats stats = new IntervalStats(); + readLocked(new AtomicFile(files[j]), stats, version); + writeLocked(new AtomicFile(new File(mIntervalDirs[i], + Long.toString(stats.beginTime))), stats, mCurrentVersion); + } catch (IOException e) { + Slog.e(TAG, + "Failed to upgrade versioned backup file : " + files[j].toString()); + throw new RuntimeException(e); + } + } + } + } + } + + private void removeVersionedBackupFiles() { + final FilenameFilter versionedBackupFileFilter = new FilenameFilter() { + @Override + public boolean accept(File dir, String name) { + return name.endsWith(VERSIONED_BAK_SUFFIX); + } + }; + + for (int i = 0; i < mIntervalDirs.length; i++) { + File[] files = mIntervalDirs[i].listFiles(versionedBackupFileFilter); + if (files != null) { + for (int j = 0; j < files.length; j++) { + if (DEBUG) { + Slog.d(TAG, + "Removing " + files[j].toString() + " for interval " + i); + } + if (!files[j].delete()) { + Slog.e(TAG, "Failed to delete file : " + files[j].toString()); + } + } + } } } @@ -357,7 +557,7 @@ public class UsageStatsDatabase { try { final AtomicFile f = mSortedStatFiles[intervalType].valueAt(fileCount - 1); IntervalStats stats = new IntervalStats(); - UsageStatsXml.read(f, stats); + readLocked(f, stats); return stats; } catch (IOException e) { Slog.e(TAG, "Failed to read usage stats file", e); @@ -379,8 +579,8 @@ public class UsageStatsDatabase { * which means you should make a copy of the data before adding it to the * <code>accumulatedResult</code> list. * - * @param stats The {@link IntervalStats} object selected. - * @param mutable Whether or not the data inside the stats object is mutable. + * @param stats The {@link IntervalStats} object selected. + * @param mutable Whether or not the data inside the stats object is mutable. * @param accumulatedResult The list to which to add extracted data. */ void combine(IntervalStats stats, boolean mutable, List<T> accumulatedResult); @@ -443,7 +643,7 @@ public class UsageStatsDatabase { } try { - UsageStatsXml.read(f, stats); + readLocked(f, stats); if (beginTime < stats.endTime) { combiner.combine(stats, false, results); } @@ -523,14 +723,9 @@ public class UsageStatsDatabase { File[] files = dir.listFiles(); if (files != null) { for (File f : files) { - String path = f.getPath(); - if (path.endsWith(BAK_SUFFIX)) { - f = new File(path.substring(0, path.length() - BAK_SUFFIX.length())); - } - long beginTime; try { - beginTime = UsageStatsXml.parseBeginTime(f); + beginTime = parseBeginTime(f); } catch (IOException e) { beginTime = 0; } @@ -542,18 +737,13 @@ public class UsageStatsDatabase { } } - private static void pruneChooserCountsOlderThan(File dir, long expiryTime) { + private void pruneChooserCountsOlderThan(File dir, long expiryTime) { File[] files = dir.listFiles(); if (files != null) { for (File f : files) { - String path = f.getPath(); - if (path.endsWith(BAK_SUFFIX)) { - f = new File(path.substring(0, path.length() - BAK_SUFFIX.length())); - } - long beginTime; try { - beginTime = UsageStatsXml.parseBeginTime(f); + beginTime = parseBeginTime(f); } catch (IOException e) { beginTime = 0; } @@ -562,7 +752,7 @@ public class UsageStatsDatabase { try { final AtomicFile af = new AtomicFile(f); final IntervalStats stats = new IntervalStats(); - UsageStatsXml.read(af, stats); + readLocked(af, stats); final int pkgCount = stats.packageStats.size(); for (int i = 0; i < pkgCount; i++) { UsageStats pkgStats = stats.packageStats.valueAt(i); @@ -570,7 +760,7 @@ public class UsageStatsDatabase { pkgStats.mChooserCounts.clear(); } } - UsageStatsXml.write(af, stats); + writeLocked(af, stats); } catch (IOException e) { Slog.e(TAG, "Failed to delete chooser counts from usage stats file", e); } @@ -579,6 +769,222 @@ public class UsageStatsDatabase { } } + + private static long parseBeginTime(AtomicFile file) throws IOException { + return parseBeginTime(file.getBaseFile()); + } + + private static long parseBeginTime(File file) throws IOException { + String name = file.getName(); + + // Parse out the digits from the the front of the file name + for (int i = 0; i < name.length(); i++) { + final char c = name.charAt(i); + if (c < '0' || c > '9') { + // found first char that is not a digit. + name = name.substring(0, i); + break; + } + } + + try { + return Long.parseLong(name); + } catch (NumberFormatException e) { + throw new IOException(e); + } + } + + private void writeLocked(AtomicFile file, IntervalStats stats) throws IOException { + writeLocked(file, stats, mCurrentVersion); + } + + private static void writeLocked(AtomicFile file, IntervalStats stats, int version) + throws IOException { + FileOutputStream fos = file.startWrite(); + try { + writeLocked(fos, stats, version); + file.finishWrite(fos); + fos = null; + } finally { + // When fos is null (successful write), this will no-op + file.failWrite(fos); + } + } + + private void writeLocked(OutputStream out, IntervalStats stats) throws IOException { + writeLocked(out, stats, mCurrentVersion); + } + + private static void writeLocked(OutputStream out, IntervalStats stats, int version) + throws IOException { + switch (version) { + case 1: + case 2: + case 3: + UsageStatsXml.write(out, stats); + break; + case 4: + UsageStatsProto.write(out, stats); + break; + default: + throw new RuntimeException( + "Unhandled UsageStatsDatabase version: " + Integer.toString(version) + + " on write."); + } + } + + private void readLocked(AtomicFile file, IntervalStats statsOut) throws IOException { + readLocked(file, statsOut, mCurrentVersion); + } + + private static void readLocked(AtomicFile file, IntervalStats statsOut, int version) + throws IOException { + try { + FileInputStream in = file.openRead(); + try { + statsOut.beginTime = parseBeginTime(file); + readLocked(in, statsOut, version); + statsOut.lastTimeSaved = file.getLastModifiedTime(); + } finally { + try { + in.close(); + } catch (IOException e) { + // Empty + } + } + } catch (FileNotFoundException e) { + Slog.e(TAG, "UsageStatsDatabase", e); + throw e; + } + // STOPSHIP: b/111422946, b/115429334 + // Everything below this comment is sanity check against the new database version. + // After the new version has soaked for some time the following should removed. + // The goal of this check is to make sure the the ProtoInputStream is properly reading from + // the UsageStats files. + final StringBuilder sb = new StringBuilder(); + final int failureLogLimit = 10; + int failures = 0; + + final int packagesSize = statsOut.packageStats.size(); + for (int i = 0; i < packagesSize; i++) { + final UsageStats stat = statsOut.packageStats.valueAt(i); + if (stat == null) { + // ArrayMap may contain null values, skip them + continue; + } + if (stat.mPackageName.isEmpty()) { + if (failures++ < failureLogLimit) { + sb.append("\nUnexpected empty usage stats package name loaded"); + } + } + if (stat.mBeginTimeStamp > statsOut.endTime) { + if (failures++ < failureLogLimit) { + sb.append("\nUnreasonable usage stats stat begin timestamp "); + sb.append(stat.mBeginTimeStamp); + sb.append(" loaded (beginTime : "); + sb.append(statsOut.beginTime); + sb.append(", endTime : "); + sb.append(statsOut.endTime); + sb.append(")"); + } + } + if (stat.mEndTimeStamp > statsOut.endTime) { + if (failures++ < failureLogLimit) { + sb.append("\nUnreasonable usage stats stat end timestamp "); + sb.append(stat.mEndTimeStamp); + sb.append(" loaded (beginTime : "); + sb.append(statsOut.beginTime); + sb.append(", endTime : "); + sb.append(statsOut.endTime); + sb.append(")"); + } + } + if (stat.mLastTimeUsed > statsOut.endTime) { + if (failures++ < failureLogLimit) { + sb.append("\nUnreasonable usage stats stat last used timestamp "); + sb.append(stat.mLastTimeUsed); + sb.append(" loaded (beginTime : "); + sb.append(statsOut.beginTime); + sb.append(", endTime : "); + sb.append(statsOut.endTime); + sb.append(")"); + } + } + } + + if (statsOut.events != null) { + final int eventSize = statsOut.events.size(); + for (int i = 0; i < eventSize; i++) { + final UsageEvents.Event event = statsOut.events.get(i); + if (event.mPackage.isEmpty()) { + if (failures++ < failureLogLimit) { + sb.append("\nUnexpected empty empty package name loaded"); + } + } + if (event.mTimeStamp < statsOut.beginTime || event.mTimeStamp > statsOut.endTime) { + if (failures++ < failureLogLimit) { + sb.append("\nUnexpected event timestamp "); + sb.append(event.mTimeStamp); + sb.append(" loaded (beginTime : "); + sb.append(statsOut.beginTime); + sb.append(", endTime : "); + sb.append(statsOut.endTime); + sb.append(")"); + } + } + if (event.mEventType < 0 || event.mEventType > UsageEvents.Event.MAX_EVENT_TYPE) { + if (failures++ < failureLogLimit) { + sb.append("\nUnexpected event type "); + sb.append(event.mEventType); + sb.append(" loaded"); + } + } + if ((event.mFlags & ~UsageEvents.Event.VALID_FLAG_BITS) != 0) { + if (failures++ < failureLogLimit) { + sb.append("\nUnexpected event flag bit 0b"); + sb.append(Integer.toBinaryString(event.mFlags)); + sb.append(" loaded"); + } + } + } + } + + if (failures != 0) { + if (failures > failureLogLimit) { + sb.append("\nFailure log limited ("); + sb.append(failures); + sb.append(" total failures found!)"); + } + sb.append("\nError found in:\n"); + sb.append(file.getBaseFile().getAbsolutePath()); + sb.append("\nPlease go to b/115429334 to help root cause this issue"); + Slog.wtf(TAG,sb.toString()); + } + } + + private void readLocked(InputStream in, IntervalStats statsOut) throws IOException { + readLocked(in, statsOut, mCurrentVersion); + } + + private static void readLocked(InputStream in, IntervalStats statsOut, int version) + throws IOException { + switch (version) { + case 1: + case 2: + case 3: + UsageStatsXml.read(in, statsOut); + break; + case 4: + UsageStatsProto.read(in, statsOut); + break; + default: + throw new RuntimeException( + "Unhandled UsageStatsDatabase version: " + Integer.toString(version) + + " on read."); + } + + } + /** * Update the stats in the database. They may not be written to disk immediately. */ @@ -596,7 +1002,7 @@ public class UsageStatsDatabase { mSortedStatFiles[intervalType].put(stats.beginTime, f); } - UsageStatsXml.write(f, stats); + writeLocked(f, stats); stats.lastTimeSaved = f.getLastModifiedTime(); } } @@ -730,7 +1136,7 @@ public class UsageStatsDatabase { throws IOException { IntervalStats stats = new IntervalStats(); try { - UsageStatsXml.read(statsFile, stats); + readLocked(statsFile, stats); } catch (IOException e) { Slog.e(TAG, "Failed to read usage stats file", e); out.writeInt(0); @@ -756,12 +1162,12 @@ public class UsageStatsDatabase { if (stats.events != null) stats.events.clear(); } - private static byte[] serializeIntervalStats(IntervalStats stats) { + private byte[] serializeIntervalStats(IntervalStats stats) { ByteArrayOutputStream baos = new ByteArrayOutputStream(); DataOutputStream out = new DataOutputStream(baos); try { out.writeLong(stats.beginTime); - UsageStatsXml.write(out, stats); + writeLocked(out, stats); } catch (IOException ioe) { Slog.d(TAG, "Serializing IntervalStats Failed", ioe); baos.reset(); @@ -769,13 +1175,13 @@ public class UsageStatsDatabase { return baos.toByteArray(); } - private static IntervalStats deserializeIntervalStats(byte[] data) { + private IntervalStats deserializeIntervalStats(byte[] data) { ByteArrayInputStream bais = new ByteArrayInputStream(data); DataInputStream in = new DataInputStream(bais); IntervalStats stats = new IntervalStats(); try { stats.beginTime = in.readLong(); - UsageStatsXml.read(in, stats); + readLocked(in, stats); } catch (IOException ioe) { Slog.d(TAG, "DeSerializing IntervalStats Failed", ioe); stats = null; diff --git a/services/usage/java/com/android/server/usage/UsageStatsProto.java b/services/usage/java/com/android/server/usage/UsageStatsProto.java new file mode 100644 index 000000000000..30d303f426bf --- /dev/null +++ b/services/usage/java/com/android/server/usage/UsageStatsProto.java @@ -0,0 +1,552 @@ +/* + * 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.usage; + +import android.app.usage.ConfigurationStats; +import android.app.usage.EventList; +import android.app.usage.UsageEvents; +import android.app.usage.UsageStats; +import android.content.res.Configuration; +import android.util.ArrayMap; + +import android.util.Slog; +import android.util.proto.ProtoInputStream; +import android.util.proto.ProtoOutputStream; + +import java.io.InputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.net.ProtocolException; +import java.util.ArrayList; +import java.util.List; + +/** + * UsageStats reader/writer for Protocol Buffer format + */ +final class UsageStatsProto { + private static String TAG = "UsageStatsProto"; + + // Static-only utility class. + private UsageStatsProto() {} + + private static List<String> readStringPool(ProtoInputStream proto) throws IOException { + + final long token = proto.start(IntervalStatsProto.STRINGPOOL); + List<String> stringPool; + if (proto.isNextField(IntervalStatsProto.StringPool.SIZE)) { + stringPool = new ArrayList(proto.readInt(IntervalStatsProto.StringPool.SIZE)); + } else { + stringPool = new ArrayList(); + } + while (proto.nextField() != ProtoInputStream.NO_MORE_FIELDS) { + switch (proto.getFieldNumber()) { + case (int) IntervalStatsProto.StringPool.STRINGS: + stringPool.add(proto.readString(IntervalStatsProto.StringPool.STRINGS)); + break; + } + } + proto.end(token); + return stringPool; + } + + private static void loadUsageStats(ProtoInputStream proto, long fieldId, + IntervalStats statsOut, List<String> stringPool) + throws IOException { + + final long token = proto.start(fieldId); + UsageStats stats; + if (proto.isNextField(IntervalStatsProto.UsageStats.PACKAGE_INDEX)) { + // Fast path reading the package name index. Most cases this should work since it is + // written first + stats = statsOut.getOrCreateUsageStats( + stringPool.get(proto.readInt(IntervalStatsProto.UsageStats.PACKAGE_INDEX) - 1)); + } else if (proto.isNextField(IntervalStatsProto.UsageStats.PACKAGE)) { + // No package index, try package name instead + stats = statsOut.getOrCreateUsageStats( + proto.readString(IntervalStatsProto.UsageStats.PACKAGE)); + } else { + // Temporarily store collected data to a UsageStats object. This is not efficient. + stats = new UsageStats(); + } + + while (proto.nextField() != ProtoInputStream.NO_MORE_FIELDS) { + switch (proto.getFieldNumber()) { + case (int) IntervalStatsProto.UsageStats.PACKAGE: + // Fast track failed from some reason, add UsageStats object to statsOut now + UsageStats tempPackage = statsOut.getOrCreateUsageStats( + proto.readString(IntervalStatsProto.UsageStats.PACKAGE)); + tempPackage.mLastTimeUsed = stats.mLastTimeUsed; + tempPackage.mTotalTimeInForeground = stats.mTotalTimeInForeground; + tempPackage.mLastEvent = stats.mLastEvent; + tempPackage.mAppLaunchCount = stats.mAppLaunchCount; + stats = tempPackage; + break; + case (int) IntervalStatsProto.UsageStats.PACKAGE_INDEX: + // Fast track failed from some reason, add UsageStats object to statsOut now + UsageStats tempPackageIndex = statsOut.getOrCreateUsageStats(stringPool.get( + proto.readInt(IntervalStatsProto.UsageStats.PACKAGE_INDEX) - 1)); + tempPackageIndex.mLastTimeUsed = stats.mLastTimeUsed; + tempPackageIndex.mTotalTimeInForeground = stats.mTotalTimeInForeground; + tempPackageIndex.mLastEvent = stats.mLastEvent; + tempPackageIndex.mAppLaunchCount = stats.mAppLaunchCount; + stats = tempPackageIndex; + break; + case (int) IntervalStatsProto.UsageStats.LAST_TIME_ACTIVE_MS: + stats.mLastTimeUsed = statsOut.beginTime + proto.readLong( + IntervalStatsProto.UsageStats.LAST_TIME_ACTIVE_MS); + break; + case (int) IntervalStatsProto.UsageStats.TOTAL_TIME_ACTIVE_MS: + stats.mTotalTimeInForeground = proto.readLong( + IntervalStatsProto.UsageStats.TOTAL_TIME_ACTIVE_MS); + break; + case (int) IntervalStatsProto.UsageStats.LAST_EVENT: + stats.mLastEvent = proto.readInt(IntervalStatsProto.UsageStats.LAST_EVENT); + break; + case (int) IntervalStatsProto.UsageStats.APP_LAUNCH_COUNT: + stats.mAppLaunchCount = proto.readInt( + IntervalStatsProto.UsageStats.APP_LAUNCH_COUNT); + break; + case (int) IntervalStatsProto.UsageStats.CHOOSER_ACTIONS: + final long chooserToken = proto.start( + IntervalStatsProto.UsageStats.CHOOSER_ACTIONS); + loadChooserCounts(proto, stats); + proto.end(chooserToken); + break; + } + } + if (stats.mLastTimeUsed == 0) { + // mLastTimeUsed was not assigned, assume default value of 0 plus beginTime; + stats.mLastTimeUsed = statsOut.beginTime; + } + proto.end(token); + } + + private static void loadCountAndTime(ProtoInputStream proto, long fieldId, + IntervalStats.EventTracker tracker) throws IOException { + final long token = proto.start(fieldId); + while (true) { + switch (proto.nextField()) { + case (int) IntervalStatsProto.CountAndTime.COUNT: + tracker.count = proto.readInt(IntervalStatsProto.CountAndTime.COUNT); + break; + case (int) IntervalStatsProto.CountAndTime.TIME_MS: + tracker.duration = proto.readLong(IntervalStatsProto.CountAndTime.TIME_MS); + break; + case ProtoInputStream.NO_MORE_FIELDS: + proto.end(token); + return; + } + } + } + + private static void loadChooserCounts(ProtoInputStream proto, UsageStats usageStats) + throws IOException { + if (usageStats.mChooserCounts == null) { + usageStats.mChooserCounts = new ArrayMap<>(); + } + String action = null; + ArrayMap<String, Integer> counts; + if (proto.isNextField(IntervalStatsProto.UsageStats.ChooserAction.NAME)) { + // Fast path reading the action name. Most cases this should work since it is written + // first + action = proto.readString(IntervalStatsProto.UsageStats.ChooserAction.NAME); + counts = usageStats.mChooserCounts.get(action); + if (counts == null) { + counts = new ArrayMap<>(); + usageStats.mChooserCounts.put(action, counts); + } + } else { + // Temporarily store collected data to an ArrayMap. This is not efficient. + counts = new ArrayMap<>(); + } + + while (true) { + switch (proto.nextField()) { + case (int) IntervalStatsProto.UsageStats.ChooserAction.NAME: + // Fast path failed from some reason, add the ArrayMap object to usageStats now + action = proto.readString(IntervalStatsProto.UsageStats.ChooserAction.NAME); + usageStats.mChooserCounts.put(action, counts); + break; + case (int) IntervalStatsProto.UsageStats.ChooserAction.COUNTS: + final long token = proto.start( + IntervalStatsProto.UsageStats.ChooserAction.COUNTS); + loadCountsForAction(proto, counts); + proto.end(token); + case ProtoInputStream.NO_MORE_FIELDS: + if (action == null) { + // default string + usageStats.mChooserCounts.put("", counts); + } + return; + } + } + } + + private static void loadCountsForAction(ProtoInputStream proto, + ArrayMap<String, Integer> counts) throws IOException { + String category = null; + int count = 0; + while (true) { + switch (proto.nextField()) { + case (int) IntervalStatsProto.UsageStats.ChooserAction.CategoryCount.NAME: + category = proto.readString( + IntervalStatsProto.UsageStats.ChooserAction.CategoryCount.NAME); + break; + case (int) IntervalStatsProto.UsageStats.ChooserAction.CategoryCount.COUNT: + count = proto.readInt( + IntervalStatsProto.UsageStats.ChooserAction.CategoryCount.COUNT); + break; + case ProtoInputStream.NO_MORE_FIELDS: + if (category == null) { + counts.put("", count); + } else { + counts.put(category, count); + } + return; + } + } + } + + private static void loadConfigStats(ProtoInputStream proto, long fieldId, + IntervalStats statsOut) throws IOException { + final long token = proto.start(fieldId); + boolean configActive = false; + final Configuration config = new Configuration(); + ConfigurationStats configStats; + if (proto.isNextField(IntervalStatsProto.Configuration.CONFIG)) { + // Fast path reading the configuration. Most cases this should work since it is + // written first + config.readFromProto(proto, IntervalStatsProto.Configuration.CONFIG); + configStats = statsOut.getOrCreateConfigurationStats(config); + } else { + // Temporarily store collected data to a ConfigurationStats object. This is not + // efficient. + configStats = new ConfigurationStats(); + } + while (true) { + switch (proto.nextField()) { + case (int) IntervalStatsProto.Configuration.CONFIG: + // Fast path failed from some reason, add ConfigStats object to statsOut now + config.readFromProto(proto, IntervalStatsProto.Configuration.CONFIG); + final ConfigurationStats temp = statsOut.getOrCreateConfigurationStats(config); + temp.mLastTimeActive = configStats.mLastTimeActive; + temp.mTotalTimeActive = configStats.mTotalTimeActive; + temp.mActivationCount = configStats.mActivationCount; + configStats = temp; + break; + case (int) IntervalStatsProto.Configuration.LAST_TIME_ACTIVE_MS: + configStats.mLastTimeActive = statsOut.beginTime + proto.readLong( + IntervalStatsProto.Configuration.LAST_TIME_ACTIVE_MS); + break; + case (int) IntervalStatsProto.Configuration.TOTAL_TIME_ACTIVE_MS: + configStats.mTotalTimeActive = proto.readLong( + IntervalStatsProto.Configuration.TOTAL_TIME_ACTIVE_MS); + break; + case (int) IntervalStatsProto.Configuration.COUNT: + configStats.mActivationCount = proto.readInt( + IntervalStatsProto.Configuration.COUNT); + break; + case (int) IntervalStatsProto.Configuration.ACTIVE: + configActive = proto.readBoolean(IntervalStatsProto.Configuration.ACTIVE); + break; + case ProtoInputStream.NO_MORE_FIELDS: + if (configStats.mLastTimeActive == 0) { + //mLastTimeActive was not assigned, assume default value of 0 plus beginTime + configStats.mLastTimeActive = statsOut.beginTime; + } + if (configActive) { + statsOut.activeConfiguration = configStats.mConfiguration; + } + proto.end(token); + return; + } + } + } + + private static void loadEvent(ProtoInputStream proto, long fieldId, IntervalStats statsOut, + List<String> stringPool) throws IOException { + final long token = proto.start(fieldId); + UsageEvents.Event event = statsOut.buildEvent(proto, stringPool); + proto.end(token); + if (event.mPackage == null) { + throw new ProtocolException("no package field present"); + } + + if (statsOut.events == null) { + statsOut.events = new EventList(); + } + statsOut.events.insert(event); + } + + private static void writeStringPool(ProtoOutputStream proto, final IntervalStats stats) + throws IOException { + final long token = proto.start(IntervalStatsProto.STRINGPOOL); + final int size = stats.mStringCache.size(); + proto.write(IntervalStatsProto.StringPool.SIZE, size); + for (int i = 0; i < size; i++) { + proto.write(IntervalStatsProto.StringPool.STRINGS, stats.mStringCache.valueAt(i)); + } + proto.end(token); + } + + private static void writeUsageStats(ProtoOutputStream proto, long fieldId, + final IntervalStats stats, final UsageStats usageStats) throws IOException { + final long token = proto.start(fieldId); + // Write the package name first, so loadUsageStats can avoid creating an extra object + final int packageIndex = stats.mStringCache.indexOf(usageStats.mPackageName); + if (packageIndex >= 0) { + proto.write(IntervalStatsProto.UsageStats.PACKAGE_INDEX, packageIndex + 1); + } else { + // Package not in Stringpool for some reason, write full string instead + Slog.w(TAG, "UsageStats package name (" + usageStats.mPackageName + + ") not found in IntervalStats string cache"); + proto.write(IntervalStatsProto.UsageStats.PACKAGE, usageStats.mPackageName); + } + proto.write(IntervalStatsProto.UsageStats.LAST_TIME_ACTIVE_MS, + usageStats.mLastTimeUsed - stats.beginTime); + proto.write(IntervalStatsProto.UsageStats.TOTAL_TIME_ACTIVE_MS, + usageStats.mTotalTimeInForeground); + proto.write(IntervalStatsProto.UsageStats.LAST_EVENT, usageStats.mLastEvent); + proto.write(IntervalStatsProto.UsageStats.APP_LAUNCH_COUNT, usageStats.mAppLaunchCount); + writeChooserCounts(proto, usageStats); + proto.end(token); + } + + private static void writeCountAndTime(ProtoOutputStream proto, long fieldId, int count, + long time) throws IOException { + final long token = proto.start(fieldId); + proto.write(IntervalStatsProto.CountAndTime.COUNT, count); + proto.write(IntervalStatsProto.CountAndTime.TIME_MS, time); + proto.end(token); + } + + + private static void writeChooserCounts(ProtoOutputStream proto, final UsageStats usageStats) + throws IOException { + if (usageStats == null || usageStats.mChooserCounts == null + || usageStats.mChooserCounts.keySet().isEmpty()) { + return; + } + final int chooserCountSize = usageStats.mChooserCounts.size(); + for (int i = 0; i < chooserCountSize; i++) { + final String action = usageStats.mChooserCounts.keyAt(i); + final ArrayMap<String, Integer> counts = usageStats.mChooserCounts.valueAt(i); + if (action == null || counts == null || counts.isEmpty()) { + continue; + } + final long token = proto.start(IntervalStatsProto.UsageStats.CHOOSER_ACTIONS); + proto.write(IntervalStatsProto.UsageStats.ChooserAction.NAME, action); + writeCountsForAction(proto, counts); + proto.end(token); + } + } + + private static void writeCountsForAction(ProtoOutputStream proto, + ArrayMap<String, Integer> counts) throws IOException { + final int countsSize = counts.size(); + for (int i = 0; i < countsSize; i++) { + String key = counts.keyAt(i); + int count = counts.valueAt(i); + if (count > 0) { + final long token = proto.start(IntervalStatsProto.UsageStats.ChooserAction.COUNTS); + proto.write(IntervalStatsProto.UsageStats.ChooserAction.CategoryCount.NAME, key); + proto.write(IntervalStatsProto.UsageStats.ChooserAction.CategoryCount.COUNT, count); + proto.end(token); + } + } + } + + private static void writeConfigStats(ProtoOutputStream proto, long fieldId, + final IntervalStats stats, final ConfigurationStats configStats, boolean isActive) + throws IOException { + final long token = proto.start(fieldId); + configStats.mConfiguration.writeToProto(proto, IntervalStatsProto.Configuration.CONFIG); + proto.write(IntervalStatsProto.Configuration.LAST_TIME_ACTIVE_MS, + configStats.mLastTimeActive - stats.beginTime); + proto.write(IntervalStatsProto.Configuration.TOTAL_TIME_ACTIVE_MS, + configStats.mTotalTimeActive); + proto.write(IntervalStatsProto.Configuration.COUNT, configStats.mActivationCount); + proto.write(IntervalStatsProto.Configuration.ACTIVE, isActive); + proto.end(token); + + } + + private static void writeEvent(ProtoOutputStream proto, long fieldId, final IntervalStats stats, + final UsageEvents.Event event) throws IOException { + final long token = proto.start(fieldId); + final int packageIndex = stats.mStringCache.indexOf(event.mPackage); + if (packageIndex >= 0) { + proto.write(IntervalStatsProto.Event.PACKAGE_INDEX, packageIndex + 1); + } else { + // Package not in Stringpool for some reason, write full string instead + Slog.w(TAG, "Usage event package name (" + event.mPackage + + ") not found in IntervalStats string cache"); + proto.write(IntervalStatsProto.Event.PACKAGE, event.mPackage); + } + if (event.mClass != null) { + final int classIndex = stats.mStringCache.indexOf(event.mClass); + if (classIndex >= 0) { + proto.write(IntervalStatsProto.Event.CLASS_INDEX, classIndex + 1); + } else { + // Class not in Stringpool for some reason, write full string instead + Slog.w(TAG, "Usage event class name (" + event.mClass + + ") not found in IntervalStats string cache"); + proto.write(IntervalStatsProto.Event.CLASS, event.mClass); + } + } + proto.write(IntervalStatsProto.Event.TIME_MS, event.mTimeStamp - stats.beginTime); + proto.write(IntervalStatsProto.Event.FLAGS, event.mFlags); + proto.write(IntervalStatsProto.Event.TYPE, event.mEventType); + switch (event.mEventType) { + case UsageEvents.Event.CONFIGURATION_CHANGE: + if (event.mConfiguration != null) { + event.mConfiguration.writeToProto(proto, IntervalStatsProto.Event.CONFIG); + } + break; + case UsageEvents.Event.SHORTCUT_INVOCATION: + if (event.mShortcutId != null) { + proto.write(IntervalStatsProto.Event.SHORTCUT_ID, event.mShortcutId); + } + break; + case UsageEvents.Event.STANDBY_BUCKET_CHANGED: + if (event.mBucketAndReason != 0) { + proto.write(IntervalStatsProto.Event.STANDBY_BUCKET, event.mBucketAndReason); + } + break; + case UsageEvents.Event.NOTIFICATION_INTERRUPTION: + if (event.mNotificationChannelId != null) { + final int channelIndex = stats.mStringCache.indexOf( + event.mNotificationChannelId); + if (channelIndex >= 0) { + proto.write(IntervalStatsProto.Event.NOTIFICATION_CHANNEL_INDEX, + channelIndex + 1); + } else { + // Channel not in Stringpool for some reason, write full string instead + Slog.w(TAG, "Usage event notification channel name (" + + event.mNotificationChannelId + + ") not found in IntervalStats string cache"); + proto.write(IntervalStatsProto.Event.NOTIFICATION_CHANNEL, + event.mNotificationChannelId); + } + } + break; + } + proto.end(token); + } + + /** + * Reads from the {@link ProtoInputStream}. + * + * @param proto The proto from which to read events. + * @param statsOut The stats object to populate with the data from the XML file. + */ + public static void read(InputStream in, IntervalStats statsOut) throws IOException { + final ProtoInputStream proto = new ProtoInputStream(in); + List<String> stringPool = null; + + statsOut.packageStats.clear(); + statsOut.configurations.clear(); + statsOut.activeConfiguration = null; + + if (statsOut.events != null) { + statsOut.events.clear(); + } + + while (true) { + switch (proto.nextField()) { + case (int) IntervalStatsProto.END_TIME_MS: + statsOut.endTime = statsOut.beginTime + proto.readLong( + IntervalStatsProto.END_TIME_MS); + break; + case (int) IntervalStatsProto.INTERACTIVE: + loadCountAndTime(proto, IntervalStatsProto.INTERACTIVE, + statsOut.interactiveTracker); + break; + case (int) IntervalStatsProto.NON_INTERACTIVE: + loadCountAndTime(proto, IntervalStatsProto.NON_INTERACTIVE, + statsOut.nonInteractiveTracker); + break; + case (int) IntervalStatsProto.KEYGUARD_SHOWN: + loadCountAndTime(proto, IntervalStatsProto.KEYGUARD_SHOWN, + statsOut.keyguardShownTracker); + break; + case (int) IntervalStatsProto.KEYGUARD_HIDDEN: + loadCountAndTime(proto, IntervalStatsProto.KEYGUARD_HIDDEN, + statsOut.keyguardHiddenTracker); + break; + case (int) IntervalStatsProto.STRINGPOOL: + stringPool = readStringPool(proto); + statsOut.mStringCache.addAll(stringPool); + break; + case (int) IntervalStatsProto.PACKAGES: + loadUsageStats(proto, IntervalStatsProto.PACKAGES, statsOut, stringPool); + break; + case (int) IntervalStatsProto.CONFIGURATIONS: + loadConfigStats(proto, IntervalStatsProto.CONFIGURATIONS, statsOut); + break; + case (int) IntervalStatsProto.EVENT_LOG: + loadEvent(proto, IntervalStatsProto.EVENT_LOG, statsOut, stringPool); + break; + case ProtoInputStream.NO_MORE_FIELDS: + if (statsOut.endTime == 0) { + // endTime not assigned, assume default value of 0 plus beginTime + statsOut.endTime = statsOut.beginTime; + } + return; + } + } + } + + /** + * Writes the stats object to an ProtoBuf file. + * + * @param proto The serializer to which to write the packageStats data. + * @param stats The stats object to write to the XML file. + */ + public static void write(OutputStream out, IntervalStats stats) throws IOException { + final ProtoOutputStream proto = new ProtoOutputStream(out); + proto.write(IntervalStatsProto.END_TIME_MS, stats.endTime - stats.beginTime); + // String pool should be written before the rest of the usage stats + writeStringPool(proto, stats); + + writeCountAndTime(proto, IntervalStatsProto.INTERACTIVE, stats.interactiveTracker.count, + stats.interactiveTracker.duration); + writeCountAndTime(proto, IntervalStatsProto.NON_INTERACTIVE, + stats.nonInteractiveTracker.count, stats.nonInteractiveTracker.duration); + writeCountAndTime(proto, IntervalStatsProto.KEYGUARD_SHOWN, + stats.keyguardShownTracker.count, stats.keyguardShownTracker.duration); + writeCountAndTime(proto, IntervalStatsProto.KEYGUARD_HIDDEN, + stats.keyguardHiddenTracker.count, stats.keyguardHiddenTracker.duration); + + final int statsCount = stats.packageStats.size(); + for (int i = 0; i < statsCount; i++) { + writeUsageStats(proto, IntervalStatsProto.PACKAGES, stats, + stats.packageStats.valueAt(i)); + } + final int configCount = stats.configurations.size(); + for (int i = 0; i < configCount; i++) { + boolean active = stats.activeConfiguration.equals(stats.configurations.keyAt(i)); + writeConfigStats(proto, IntervalStatsProto.CONFIGURATIONS, stats, + stats.configurations.valueAt(i), active); + } + final int eventCount = stats.events != null ? stats.events.size() : 0; + for (int i = 0; i < eventCount; i++) { + writeEvent(proto, IntervalStatsProto.EVENT_LOG, stats, stats.events.get(i)); + } + + proto.flush(); + } +} diff --git a/services/usage/java/com/android/server/usage/UsageStatsXml.java b/services/usage/java/com/android/server/usage/UsageStatsXml.java index e7db74149b4a..f8d1113e8460 100644 --- a/services/usage/java/com/android/server/usage/UsageStatsXml.java +++ b/services/usage/java/com/android/server/usage/UsageStatsXml.java @@ -19,6 +19,9 @@ package com.android.server.usage; import android.util.AtomicFile; import android.util.Slog; import android.util.Xml; +import android.util.proto.ProtoInputStream; +import android.util.proto.ProtoOutputStream; + import com.android.internal.util.FastXmlSerializer; import com.android.internal.util.XmlUtils; import org.xmlpull.v1.XmlPullParser; @@ -33,61 +36,7 @@ public class UsageStatsXml { private static final String VERSION_ATTR = "version"; static final String CHECKED_IN_SUFFIX = "-c"; - public static long parseBeginTime(AtomicFile file) throws IOException { - return parseBeginTime(file.getBaseFile()); - } - - public static long parseBeginTime(File file) throws IOException { - String name = file.getName(); - - // Eat as many occurrences of -c as possible. This is due to a bug where -c - // would be appended more than once to a checked-in file, causing a crash - // on boot when indexing files since Long.parseLong() will puke on anything but - // a number. - while (name.endsWith(CHECKED_IN_SUFFIX)) { - name = name.substring(0, name.length() - CHECKED_IN_SUFFIX.length()); - } - - try { - return Long.parseLong(name); - } catch (NumberFormatException e) { - throw new IOException(e); - } - } - - public static void read(AtomicFile file, IntervalStats statsOut) throws IOException { - try { - FileInputStream in = file.openRead(); - try { - statsOut.beginTime = parseBeginTime(file); - read(in, statsOut); - statsOut.lastTimeSaved = file.getLastModifiedTime(); - } finally { - try { - in.close(); - } catch (IOException e) { - // Empty - } - } - } catch (FileNotFoundException e) { - Slog.e(TAG, "UsageStats Xml", e); - throw e; - } - } - - public static void write(AtomicFile file, IntervalStats stats) throws IOException { - FileOutputStream fos = file.startWrite(); - try { - write(fos, stats); - file.finishWrite(fos); - fos = null; - } finally { - // When fos is null (successful write), this will no-op - file.failWrite(fos); - } - } - - static void read(InputStream in, IntervalStats statsOut) throws IOException { + public static void read(InputStream in, IntervalStats statsOut) throws IOException { XmlPullParser parser = Xml.newPullParser(); try { parser.setInput(in, "utf-8"); @@ -113,7 +62,7 @@ public class UsageStatsXml { } } - static void write(OutputStream out, IntervalStats stats) throws IOException { + public static void write(OutputStream out, IntervalStats stats) throws IOException { FastXmlSerializer xml = new FastXmlSerializer(); xml.setOutput(out, "utf-8"); xml.startDocument("utf-8", true); diff --git a/services/usage/java/com/android/server/usage/UsageStatsXmlV1.java b/services/usage/java/com/android/server/usage/UsageStatsXmlV1.java index 6a1e97a51453..a68f9d385ca5 100644 --- a/services/usage/java/com/android/server/usage/UsageStatsXmlV1.java +++ b/services/usage/java/com/android/server/usage/UsageStatsXmlV1.java @@ -196,11 +196,7 @@ final class UsageStatsXmlV1 { event.mNotificationChannelId = (channelId != null) ? channelId.intern() : null; break; } - - if (statsOut.events == null) { - statsOut.events = new EventList(); - } - statsOut.events.insert(event); + statsOut.addEvent(event); } private static void writeUsageStats(XmlSerializer xml, final IntervalStats stats, diff --git a/services/usage/java/com/android/server/usage/UserUsageStatsService.java b/services/usage/java/com/android/server/usage/UserUsageStatsService.java index 9b194e9ec638..1a8aba085d24 100644 --- a/services/usage/java/com/android/server/usage/UserUsageStatsService.java +++ b/services/usage/java/com/android/server/usage/UserUsageStatsService.java @@ -176,12 +176,8 @@ class UserUsageStatsService { currentDailyStats.activeConfiguration, newFullConfig); } - // Add the event to the daily list. - if (currentDailyStats.events == null) { - currentDailyStats.events = new EventList(); - } if (event.mEventType != UsageEvents.Event.SYSTEM_INTERACTION) { - currentDailyStats.events.insert(event); + currentDailyStats.addEvent(event); } boolean incrementAppLaunch = false; diff --git a/startop/tools/view_compiler/Android.bp b/startop/tools/view_compiler/Android.bp new file mode 100644 index 000000000000..c3e91849e636 --- /dev/null +++ b/startop/tools/view_compiler/Android.bp @@ -0,0 +1,49 @@ +// +// 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. +// + +cc_library_host_static { + name: "libviewcompiler", + srcs: [ + "java_lang_builder.cc", + "util.cc", + ], + static_libs: [ + "libbase" + ] +} + +cc_binary_host { + name: "viewcompiler", + srcs: [ + "main.cc", + ], + static_libs: [ + "libbase", + "libtinyxml2", + "libgflags", + "libviewcompiler", + ], +} + +cc_test_host { + name: "view-compiler-tests", + srcs: [ + "util_test.cc", + ], + static_libs: [ + "libviewcompiler", + ] +} diff --git a/startop/tools/view_compiler/README.md b/startop/tools/view_compiler/README.md new file mode 100644 index 000000000000..56595016cbb9 --- /dev/null +++ b/startop/tools/view_compiler/README.md @@ -0,0 +1,25 @@ +# View Compiler + +This directory contains an experimental compiler for layout files. + +It will take a layout XML file and produce a CompiledLayout.java file with a +specialized layout inflation function. + +To use it, let's assume you had a layout in `my_layout.xml` and your app was in +the Java language package `com.example.myapp`. Run the following command: + + viewcompiler my_layout.xml --package com.example.myapp --out CompiledView.java + +This will produce a `CompiledView.java`, which can then be compiled into your +Android app. Then to use it, in places where you would have inflated +`R.layouts.my_layout`, instead call `CompiledView.inflate`. + +Precompiling views like this generally improves the time needed to inflate them. + +This tool is still in its early stages and has a number of limitations. +* Currently only one layout can be compiled at a time. +* `merge` and `include` nodes are not supported. +* View compilation is a manual process that requires code changes in the + application. +* This only works for apps that do not use a custom layout inflater. +* Other limitations yet to be discovered. diff --git a/startop/tools/view_compiler/TEST_MAPPING b/startop/tools/view_compiler/TEST_MAPPING new file mode 100644 index 000000000000..cc4b17a7a65a --- /dev/null +++ b/startop/tools/view_compiler/TEST_MAPPING @@ -0,0 +1,7 @@ +{ + "presubmit": [ + { + "name": "view-compiler-tests" + } + ] +} diff --git a/startop/tools/view_compiler/java_lang_builder.cc b/startop/tools/view_compiler/java_lang_builder.cc new file mode 100644 index 000000000000..0b8754fc7096 --- /dev/null +++ b/startop/tools/view_compiler/java_lang_builder.cc @@ -0,0 +1,115 @@ +/* + * 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. + */ + +#include "java_lang_builder.h" + +#include "android-base/stringprintf.h" + +using android::base::StringPrintf; +using std::string; + +void JavaLangViewBuilder::Start() const { + out_ << StringPrintf("package %s;\n", package_.c_str()) + << "import android.content.Context;\n" + "import android.content.res.Resources;\n" + "import android.content.res.XmlResourceParser;\n" + "import android.util.AttributeSet;\n" + "import android.util.Xml;\n" + "import android.view.*;\n" + "import android.widget.*;\n" + "\n" + "public final class CompiledView {\n" + "\n" + "static <T extends View> T createView(Context context, AttributeSet attrs, View parent, " + "String name, LayoutInflater.Factory factory, LayoutInflater.Factory2 factory2) {" + "\n" + " if (factory2 != null) {\n" + " return (T)factory2.onCreateView(parent, name, context, attrs);\n" + " } else if (factory != null) {\n" + " return (T)factory.onCreateView(name, context, attrs);\n" + " }\n" + // TODO: find a way to call the private factory + " return null;\n" + "}\n" + "\n" + " public static View inflate(Context context) {\n" + " try {\n" + " LayoutInflater inflater = LayoutInflater.from(context);\n" + " LayoutInflater.Factory factory = inflater.getFactory();\n" + " LayoutInflater.Factory2 factory2 = inflater.getFactory2();\n" + " Resources res = context.getResources();\n" + << StringPrintf(" XmlResourceParser xml = res.getLayout(%s.R.layout.%s);\n", + package_.c_str(), + layout_name_.c_str()) + << " AttributeSet attrs = Xml.asAttributeSet(xml);\n" + // The Java-language XmlPullParser needs a call to next to find the start document tag. + " xml.next(); // start document\n"; +} + +void JavaLangViewBuilder::Finish() const { + out_ << " } catch (Exception e) {\n" + " return null;\n" + " }\n" // end try + " }\n" // end inflate + "}\n"; // end CompiledView +} + +void JavaLangViewBuilder::StartView(const string& class_name) { + const string view_var = MakeVar("view"); + const string layout_var = MakeVar("layout"); + std::string parent = "null"; + if (!view_stack_.empty()) { + const StackEntry& parent_entry = view_stack_.back(); + parent = parent_entry.view_var; + } + out_ << " xml.next(); // <" << class_name << ">\n" + << StringPrintf(" %s %s = createView(context, attrs, %s, \"%s\", factory, factory2);\n", + class_name.c_str(), + view_var.c_str(), + parent.c_str(), + class_name.c_str()) + << StringPrintf(" if (%s == null) %s = new %s(context, attrs);\n", + view_var.c_str(), + view_var.c_str(), + class_name.c_str()); + if (!view_stack_.empty()) { + out_ << StringPrintf(" ViewGroup.LayoutParams %s = %s.generateLayoutParams(attrs);\n", + layout_var.c_str(), + parent.c_str()); + } + view_stack_.push_back({class_name, view_var, layout_var}); +} + +void JavaLangViewBuilder::FinishView() { + const StackEntry var = view_stack_.back(); + view_stack_.pop_back(); + if (!view_stack_.empty()) { + const string& parent = view_stack_.back().view_var; + out_ << StringPrintf(" xml.next(); // </%s>\n", var.class_name.c_str()) + << StringPrintf(" %s.addView(%s, %s);\n", + parent.c_str(), + var.view_var.c_str(), + var.layout_params_var.c_str()); + } else { + out_ << StringPrintf(" return %s;\n", var.view_var.c_str()); + } +} + +const std::string JavaLangViewBuilder::MakeVar(std::string prefix) { + std::stringstream v; + v << prefix << view_id_++; + return v.str(); +} diff --git a/startop/tools/view_compiler/java_lang_builder.h b/startop/tools/view_compiler/java_lang_builder.h new file mode 100644 index 000000000000..c8d20b23cd13 --- /dev/null +++ b/startop/tools/view_compiler/java_lang_builder.h @@ -0,0 +1,65 @@ +/* + * 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. + */ +#ifndef JAVA_LANG_BUILDER_H_ +#define JAVA_LANG_BUILDER_H_ + +#include <iostream> +#include <sstream> +#include <vector> + +// Build Java language code to instantiate views. +// +// This has a very small interface to make it easier to generate additional +// backends, such as a direct-to-DEX version. +class JavaLangViewBuilder { + public: + JavaLangViewBuilder(std::string package, std::string layout_name, std::ostream& out = std::cout) + : package_(package), layout_name_(layout_name), out_(out) {} + + // Begin generating a class. Adds the package boilerplate, etc. + void Start() const; + // Finish generating a class, closing off any open curly braces, etc. + void Finish() const; + + // Begin creating a view (i.e. process the opening tag) + void StartView(const std::string& class_name); + // Finish a view, after all of its child nodes have been processed. + void FinishView(); + + private: + const std::string MakeVar(std::string prefix); + + std::string const package_; + std::string const layout_name_; + + std::ostream& out_; + + size_t view_id_ = 0; + + struct StackEntry { + // The class name for this view object + const std::string class_name; + + // The variable name that is holding the view object + const std::string view_var; + + // The variable name that holds the object's layout parameters + const std::string layout_params_var; + }; + std::vector<StackEntry> view_stack_; +}; + +#endif // JAVA_LANG_BUILDER_H_ diff --git a/startop/tools/view_compiler/main.cc b/startop/tools/view_compiler/main.cc new file mode 100644 index 000000000000..0ad7e24feb3b --- /dev/null +++ b/startop/tools/view_compiler/main.cc @@ -0,0 +1,105 @@ +/* + * 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. + */ + +#include "gflags/gflags.h" + +#include "java_lang_builder.h" +#include "util.h" + +#include "tinyxml2.h" + +#include <fstream> +#include <iostream> +#include <sstream> +#include <string> +#include <vector> + +using namespace tinyxml2; +using std::string; + +constexpr char kStdoutFilename[]{"stdout"}; + +DEFINE_string(package, "", "The package name for the generated class (required)"); +DEFINE_string(out, kStdoutFilename, "Where to write the generated class"); + +namespace { +class ViewCompilerXmlVisitor : public XMLVisitor { + public: + ViewCompilerXmlVisitor(JavaLangViewBuilder* builder) : builder_(builder) {} + + bool VisitEnter(const XMLDocument& /*doc*/) override { + builder_->Start(); + return true; + } + + bool VisitExit(const XMLDocument& /*doc*/) override { + builder_->Finish(); + return true; + } + + bool VisitEnter(const XMLElement& element, const XMLAttribute* /*firstAttribute*/) override { + builder_->StartView(element.Name()); + return true; + } + + bool VisitExit(const XMLElement& /*element*/) override { + builder_->FinishView(); + return true; + } + + private: + JavaLangViewBuilder* builder_; +}; +} // end namespace + +int main(int argc, char** argv) { + constexpr size_t kProgramName = 0; + constexpr size_t kFileNameParam = 1; + constexpr size_t kNumRequiredArgs = 2; + + gflags::SetUsageMessage( + "Compile XML layout files into equivalent Java language code\n" + "\n" + " example usage: viewcompiler layout.xml --package com.example.androidapp"); + gflags::ParseCommandLineFlags(&argc, &argv, /*remove_flags*/ true); + + gflags::CommandLineFlagInfo cmd = gflags::GetCommandLineFlagInfoOrDie("package"); + if (argc != kNumRequiredArgs || cmd.is_default) { + gflags::ShowUsageWithFlags(argv[kProgramName]); + return 1; + } + + const char* const filename = argv[kFileNameParam]; + const string layout_name = FindLayoutNameFromFilename(filename); + + // We want to generate Java language code to inflate exactly this layout. This means + // generating code to walk the resource XML too. + + XMLDocument xml; + xml.LoadFile(filename); + + std::ofstream outfile; + if (FLAGS_out != kStdoutFilename) { + outfile.open(FLAGS_out); + } + JavaLangViewBuilder builder{ + FLAGS_package, layout_name, FLAGS_out == kStdoutFilename ? std::cout : outfile}; + + ViewCompilerXmlVisitor visitor{&builder}; + xml.Accept(&visitor); + + return 0; +}
\ No newline at end of file diff --git a/startop/tools/view_compiler/util.cc b/startop/tools/view_compiler/util.cc new file mode 100644 index 000000000000..69df41dff3d7 --- /dev/null +++ b/startop/tools/view_compiler/util.cc @@ -0,0 +1,32 @@ +/* + * 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. + */ + +#include "util.h" + +using std::string; + +// TODO: see if we can borrow this from somewhere else, like aapt2. +string FindLayoutNameFromFilename(const string& filename) { + size_t start = filename.rfind("/"); + if (start == string::npos) { + start = 0; + } else { + start++; // advance past '/' character + } + size_t end = filename.find(".", start); + + return filename.substr(start, end - start); +} diff --git a/startop/tools/view_compiler/util.h b/startop/tools/view_compiler/util.h new file mode 100644 index 000000000000..03e093920bfa --- /dev/null +++ b/startop/tools/view_compiler/util.h @@ -0,0 +1,23 @@ +/* + * 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. + */ +#ifndef UTIL_H_ +#define UTIL_H_ + +#include <string> + +std::string FindLayoutNameFromFilename(const std::string& filename); + +#endif // UTIL_H_ diff --git a/startop/tools/view_compiler/util_test.cc b/startop/tools/view_compiler/util_test.cc new file mode 100644 index 000000000000..d1540d3a6e43 --- /dev/null +++ b/startop/tools/view_compiler/util_test.cc @@ -0,0 +1,28 @@ +/* + * 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. + */ + +#include "util.h" + +#include "gtest/gtest.h" + +using std::string; + +TEST(UtilTest, FindLayoutNameFromFilename) { + EXPECT_EQ("bar", ::FindLayoutNameFromFilename("foo/bar.xml")); + EXPECT_EQ("bar", ::FindLayoutNameFromFilename("bar.xml")); + EXPECT_EQ("bar", ::FindLayoutNameFromFilename("./foo/bar.xml")); + EXPECT_EQ("bar", ::FindLayoutNameFromFilename("/foo/bar.xml")); +} diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java index 8c37a21afa50..d33a537f2194 100644 --- a/telecomm/java/android/telecom/TelecomManager.java +++ b/telecomm/java/android/telecom/TelecomManager.java @@ -1905,6 +1905,22 @@ public class TelecomManager { return false; } + /** + * Handles {@link Intent#ACTION_CALL} intents trampolined from UserCallActivity. + * @param intent The {@link Intent#ACTION_CALL} intent to handle. + * @hide + */ + public void handleCallIntent(Intent intent) { + try { + if (isServiceConnected()) { + getTelecomService().handleCallIntent(intent); + } + } catch (RemoteException e) { + Log.e(TAG, "RemoteException handleCallIntent: " + e); + } + + } + private ITelecomService getTelecomService() { if (mTelecomServiceOverride != null) { return mTelecomServiceOverride; diff --git a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl index 38247bc80e5c..df7d6832833a 100644 --- a/telecomm/java/com/android/internal/telecom/ITelecomService.aidl +++ b/telecomm/java/com/android/internal/telecom/ITelecomService.aidl @@ -284,4 +284,9 @@ interface ITelecomService { * @see TelecomServiceImpl#isInEmergencyCall */ boolean isInEmergencyCall(); + + /** + * @see TelecomServiceImpl#handleCallIntent + */ + void handleCallIntent(in Intent intent); } diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java index 995418ee706c..57b652e4cc59 100644 --- a/telephony/java/android/telephony/CarrierConfigManager.java +++ b/telephony/java/android/telephony/CarrierConfigManager.java @@ -271,6 +271,14 @@ public class CarrierConfigManager { KEY_HIDE_CARRIER_NETWORK_SETTINGS_BOOL = "hide_carrier_network_settings_bool"; /** + * Do only allow auto selection in Advanced Network Settings when in home network. + * Manual selection is allowed when in roaming network. + * @hide + */ + public static final String + KEY_ONLY_AUTO_SELECT_IN_HOME_NETWORK_BOOL = "only_auto_select_in_home_network"; + + /** * Control whether users receive a simplified network settings UI and improved network * selection. */ @@ -2181,6 +2189,7 @@ public class CarrierConfigManager { sDefaults.putBoolean(KEY_ENABLE_DIALER_KEY_VIBRATION_BOOL, true); sDefaults.putBoolean(KEY_HAS_IN_CALL_NOISE_SUPPRESSION_BOOL, false); sDefaults.putBoolean(KEY_HIDE_CARRIER_NETWORK_SETTINGS_BOOL, false); + sDefaults.putBoolean(KEY_ONLY_AUTO_SELECT_IN_HOME_NETWORK_BOOL, false); sDefaults.putBoolean(KEY_SIMPLIFIED_NETWORK_SETTINGS_BOOL, false); sDefaults.putBoolean(KEY_HIDE_SIM_LOCK_SETTINGS_BOOL, false); diff --git a/telephony/java/android/telephony/NeighboringCellInfo.java b/telephony/java/android/telephony/NeighboringCellInfo.java index 8e99518d78b8..5e4518f67538 100644 --- a/telephony/java/android/telephony/NeighboringCellInfo.java +++ b/telephony/java/android/telephony/NeighboringCellInfo.java @@ -32,7 +32,11 @@ import android.os.Parcelable; /** * Represents the neighboring cell information, including * Received Signal Strength and Cell ID location. + * + * @deprecated This class should not be used by anyone targeting SDK level 29 (Q) or higher. + * Instead callers should use {@Link android.telephony.CellInfo}. */ +@Deprecated public class NeighboringCellInfo implements Parcelable { /** diff --git a/telephony/java/android/telephony/PhoneStateListener.java b/telephony/java/android/telephony/PhoneStateListener.java index 498be968265f..3ea018af97cf 100644 --- a/telephony/java/android/telephony/PhoneStateListener.java +++ b/telephony/java/android/telephony/PhoneStateListener.java @@ -281,6 +281,16 @@ public class PhoneStateListener { */ public static final int LISTEN_PHONE_CAPABILITY_CHANGE = 0x00200000; + /** + * Listen for changes to preferred data subId. + * See {@link SubscriptionManager#setPreferredData(int)} + * for more details. + * + * @see #onPreferredDataSubIdChanged + * @hide + */ + public static final int LISTEN_PREFERRED_DATA_SUBID_CHANGE = 0x00400000; + /* * Subscription used to listen to the phone state changes * @hide @@ -407,6 +417,9 @@ public class PhoneStateListener { PhoneStateListener.this.onPhoneCapabilityChanged( (PhoneCapability) msg.obj); break; + case LISTEN_PREFERRED_DATA_SUBID_CHANGE: + PhoneStateListener.this.onPreferredDataSubIdChanged((int) msg.obj); + break; } } }; @@ -647,6 +660,18 @@ public class PhoneStateListener { } /** + * Callback invoked when preferred data subId changes. Requires + * the READ_PRIVILEGED_PHONE_STATE permission. + * @param subId the new preferred data subId. If it's INVALID_SUBSCRIPTION_ID, + * it means it's unset and defaultDataSub is used to determine which + * modem is preferred. + * @hide + */ + public void onPreferredDataSubIdChanged(int subId) { + // default implementation empty + } + + /** * Callback invoked when telephony has received notice from a carrier * app that a network action that could result in connectivity loss * has been requested by an app using @@ -777,6 +802,11 @@ public class PhoneStateListener { public void onPhoneCapabilityChanged(PhoneCapability capability) { send(LISTEN_PHONE_CAPABILITY_CHANGE, 0, 0, capability); } + + public void onPreferredDataSubIdChanged(int subId) { + send(LISTEN_PREFERRED_DATA_SUBID_CHANGE, 0, 0, subId); + } + } /** diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java index d1091f489f14..cc143d63d624 100644 --- a/telephony/java/android/telephony/SubscriptionManager.java +++ b/telephony/java/android/telephony/SubscriptionManager.java @@ -2157,10 +2157,11 @@ public class SubscriptionManager { * It's also usually what we set up internet connection on. * * PreferredData overwrites user setting of default data subscription. And it's used - * by ANAS or carrier apps to switch primary and CBRS subscription dynamically in multi-SIM - * devices. + * by AlternativeNetworkAccessService or carrier apps to switch primary and CBRS + * subscription dynamically in multi-SIM devices. * - * @param slotId which slot is preferred to for cellular data. + * @param slotId which slot is preferred to for cellular data. If it's INVALID, it means + * it's unset and defaultDataSubId is used to determine which modem is preferred. * @hide * */ diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java index 824533d59bf5..ea9ac39c8111 100644 --- a/telephony/java/android/telephony/TelephonyManager.java +++ b/telephony/java/android/telephony/TelephonyManager.java @@ -1590,6 +1590,7 @@ public class TelephonyManager { * * @return List of NeighboringCellInfo or null if info unavailable. * + * @removed * @deprecated Use {@link #getAllCellInfo} which returns a superset of the information * from NeighboringCellInfo, including LTE cell information. */ diff --git a/telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl b/telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl index 1ebb6976b45e..38a1bc73c94d 100644 --- a/telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl +++ b/telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl @@ -52,5 +52,6 @@ oneway interface IPhoneStateListener { void onCarrierNetworkChange(in boolean active); void onUserMobileDataStateChanged(in boolean enabled); void onPhoneCapabilityChanged(in PhoneCapability capability); + void onPreferredDataSubIdChanged(in int subId); } diff --git a/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl b/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl index 43d56b39e0c4..c03065c34ca8 100644 --- a/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl +++ b/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl @@ -79,4 +79,5 @@ interface ITelephonyRegistry { void notifyCarrierNetworkChange(in boolean active); void notifyUserMobileDataStateChangedForPhoneId(in int phoneId, in int subId, in boolean state); void notifyPhoneCapabilityChanged(in PhoneCapability capability); + void notifyPreferredDataSubIdChanged(int preferredSubId); } diff --git a/telephony/java/com/android/internal/telephony/PhoneConstants.java b/telephony/java/com/android/internal/telephony/PhoneConstants.java index f9de776f9a7b..21f3b92c6c4f 100644 --- a/telephony/java/com/android/internal/telephony/PhoneConstants.java +++ b/telephony/java/com/android/internal/telephony/PhoneConstants.java @@ -176,6 +176,10 @@ public class PhoneConstants { // FIXME: This is used to pass a subId via intents, we need to look at its usage, which is // FIXME: extensive, and see if this should be an array of all active subId's or ...? + /** + * @Deprecated use {@link android.telephony.SubscriptionManager#EXTRA_SUBSCRIPTION_INDEX} + * instead. + */ public static final String SUBSCRIPTION_KEY = "subscription"; public static final String SUB_SETTING = "subSettings"; diff --git a/test-base/Android.bp b/test-base/Android.bp index 0b8a02a815d9..4d765d3e5f3f 100644 --- a/test-base/Android.bp +++ b/test-base/Android.bp @@ -37,7 +37,8 @@ java_sdk_library { "junit.framework", ], - droiddoc_options: ["stubsourceonly"], + droiddoc_options: ["-stubsourceonly"], + metalava_enabled: false, compile_dex: true, } diff --git a/test-mock/Android.bp b/test-mock/Android.bp index 5eba01779f46..37158e5fe0b9 100644 --- a/test-mock/Android.bp +++ b/test-mock/Android.bp @@ -26,5 +26,6 @@ java_sdk_library { ], srcs_lib_whitelist_pkgs: ["android"], + metalava_enabled: false, compile_dex: true, } diff --git a/test-runner/Android.bp b/test-runner/Android.bp index ea615b920df6..0a0d50cc330c 100644 --- a/test-runner/Android.bp +++ b/test-runner/Android.bp @@ -40,7 +40,8 @@ java_sdk_library { "junit.textui", ], - droiddoc_options: ["stubsourceonly"], + droiddoc_options: ["-stubsourceonly"], + metalava_enabled: false, compile_dex: true } diff --git a/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java b/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java index 1f60b71c3c0f..976848c60dc7 100644 --- a/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java +++ b/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java @@ -106,9 +106,8 @@ public class AppLaunch extends InstrumentationTestCase { private static final String DROP_CACHE_SCRIPT = "/data/local/tmp/dropCache.sh"; private static final String APP_LAUNCH_CMD = "am start -W -n"; private static final String SUCCESS_MESSAGE = "Status: ok"; - private static final String WARNING_MESSAGE = "Warning:"; + private static final String TOTAL_TIME_MESSAGE = "TotalTime:"; private static final String COMPILE_SUCCESS = "Success"; - private static final String THIS_TIME = "ThisTime:"; private static final String LAUNCH_ITERATION = "LAUNCH_ITERATION - %d"; private static final String TRACE_ITERATION = "TRACE_ITERATION-%d"; private static final String LAUNCH_ITERATION_PREFIX = "LAUNCH_ITERATION"; @@ -814,15 +813,13 @@ public class AppLaunch extends InstrumentationTestCase { String launchTime = "-1"; String cpuCycles = "-1"; String majorFaults = "-1"; - boolean coldLaunchSuccess = false; - boolean hotLaunchSuccess = false; + boolean launchSuccess = false; try { InputStream inputStream = new FileInputStream(parcelDesc.getFileDescriptor()); /* SAMPLE OUTPUT : Cold launch Starting: Intent { cmp=com.google.android.calculator/com.android.calculator2.Calculator } Status: ok Activity: com.google.android.calculator/com.android.calculator2.Calculator - ThisTime: 357 TotalTime: 357 WaitTime: 377 Complete*/ @@ -831,7 +828,6 @@ public class AppLaunch extends InstrumentationTestCase { Warning: Activity not started, its current task has been brought to the front Status: ok Activity: com.google.android.calculator/com.android.calculator2.CalculatorGoogle - ThisTime: 60 TotalTime: 60 WaitTime: 67 Complete*/ @@ -842,54 +838,37 @@ public class AppLaunch extends InstrumentationTestCase { Total test time,1.462129,seconds,*/ BufferedReader bufferedReader = new BufferedReader(new InputStreamReader( inputStream)); - String line = null; - int lineCount = 1; + String line; mBufferedWriter.newLine(); mBufferedWriter.write(headerInfo); mBufferedWriter.newLine(); while ((line = bufferedReader.readLine()) != null) { - if (lineCount == 2 && line.startsWith(SUCCESS_MESSAGE)) { - coldLaunchSuccess = true; + mBufferedWriter.write(line); + mBufferedWriter.newLine(); + if (line.startsWith(SUCCESS_MESSAGE)) { + launchSuccess = true; } - if (lineCount == 2 && line.startsWith(WARNING_MESSAGE)) { - hotLaunchSuccess = true; + if (!launchSuccess) { + continue; } // Parse TotalTime which is the launch time - if (coldLaunchSuccess && lineCount == 5) { - String launchSplit[] = line.split(":"); - launchTime = launchSplit[1].trim(); - } - if (hotLaunchSuccess && lineCount == 6) { + if (line.startsWith(TOTAL_TIME_MESSAGE)) { String launchSplit[] = line.split(":"); launchTime = launchSplit[1].trim(); } if (mSimplePerfAppOnly) { - // Parse simpleperf output. - if ((lineCount == 9 && coldLaunchSuccess) - || (lineCount == 10 && hotLaunchSuccess)) { - if (!line.contains("cpu-cycles")) { - Log.e(TAG, "Error in simpleperf output"); - } else { - cpuCycles = line.split(",")[0].trim(); - } - } else if ((lineCount == 10 && coldLaunchSuccess) - || (lineCount == 11 && hotLaunchSuccess)) { - if (!line.contains("major-faults")) { - Log.e(TAG, "Error in simpleperf output"); - } else { - majorFaults = line.split(",")[0].trim(); - } + if (line.contains(",cpu-cycles,")) { + cpuCycles = line.split(",")[0].trim(); + } else if (line.contains(",major-faults,")) { + majorFaults = line.split(",")[0].trim(); } } - mBufferedWriter.write(line); - mBufferedWriter.newLine(); - lineCount++; } mBufferedWriter.flush(); inputStream.close(); } catch (IOException e) { - Log.w(TAG, "Error writing the launch file", e); + Log.w(TAG, "Error parsing launch time and writing to file", e); } return new AppLaunchResult(launchTime, cpuCycles, majorFaults); } diff --git a/tests/RemoteDisplayProvider/Android.mk b/tests/RemoteDisplayProvider/Android.mk index e827ec20ae3e..43bf0243b55b 100644 --- a/tests/RemoteDisplayProvider/Android.mk +++ b/tests/RemoteDisplayProvider/Android.mk @@ -18,9 +18,9 @@ LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_PACKAGE_NAME := RemoteDisplayProviderTest LOCAL_MODULE_TAGS := tests -LOCAL_SDK_VERSION := current +LOCAL_SDK_VERSION := system_current LOCAL_SRC_FILES := $(call all-java-files-under, src) LOCAL_RESOURCE_DIR = $(LOCAL_PATH)/res -LOCAL_JAVA_LIBRARIES := com.android.media.remotedisplay.stubs +LOCAL_JAVA_LIBRARIES := com.android.media.remotedisplay LOCAL_CERTIFICATE := platform include $(BUILD_PACKAGE) diff --git a/tests/net/Android.mk b/tests/net/Android.mk index e529b933bb80..750e2fb6f6b4 100644 --- a/tests/net/Android.mk +++ b/tests/net/Android.mk @@ -38,6 +38,7 @@ LOCAL_JNI_SHARED_LIBRARIES := \ libbacktrace \ libbase \ libbinder \ + libbinderthreadstate \ libc++ \ libcrypto \ libcutils \ diff --git a/tests/net/java/android/net/netlink/InetDiagSocketTest.java b/tests/net/java/android/net/netlink/InetDiagSocketTest.java index 39ecb7e5a45e..122edbaf078c 100644 --- a/tests/net/java/android/net/netlink/InetDiagSocketTest.java +++ b/tests/net/java/android/net/netlink/InetDiagSocketTest.java @@ -69,17 +69,12 @@ public class InetDiagSocketTest { private ConnectivityManager mCm; private Context mContext; private final static int SOCKET_TIMEOUT_MS = 100; - private boolean mInetDiagUdpEnabled; @Before public void setUp() throws Exception { Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation(); mContext = instrumentation.getTargetContext(); mCm = (ConnectivityManager) mContext.getSystemService(Context.CONNECTIVITY_SERVICE); - int expectedUid = Process.myUid(); - UdpConnection udp = new UdpConnection("127.0.0.1", "127.0.0.2"); - int uid = mCm.getConnectionOwnerUid(udp.protocol, udp.local, udp.remote); - mInetDiagUdpEnabled = (uid == expectedUid); } private class Connection { @@ -188,11 +183,6 @@ public class InetDiagSocketTest { tcp.close(); /** - * TODO: STOPSHIP: Always test for UDP, do not allow opt-out. - */ - if (!mInetDiagUdpEnabled) return; - - /** * For UDP connections, either a complete match {protocol, local, remote} or a * partial match {protocol, local} should return a valid UID. */ diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java index fceaabddfec2..1c77fcc568f6 100644 --- a/tests/net/java/com/android/server/ConnectivityServiceTest.java +++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java @@ -1070,13 +1070,13 @@ public class ConnectivityServiceTest { // Ensure that the default setting for Captive Portals is used for most tests setCaptivePortalMode(Settings.Global.CAPTIVE_PORTAL_MODE_PROMPT); - setMobileDataAlwaysOn(false); + setAlwaysOnNetworks(false); setPrivateDnsSettings(PRIVATE_DNS_MODE_OFF, "ignored.example.com"); } @After public void tearDown() throws Exception { - setMobileDataAlwaysOn(false); + setAlwaysOnNetworks(false); if (mCellNetworkAgent != null) { mCellNetworkAgent.disconnect(); mCellNetworkAgent = null; @@ -2027,7 +2027,7 @@ public class ConnectivityServiceTest { @Test public void testNetworkGoesIntoBackgroundAfterLinger() { - setMobileDataAlwaysOn(true); + setAlwaysOnNetworks(true); NetworkRequest request = new NetworkRequest.Builder() .clearCapabilities() .build(); @@ -2772,10 +2772,10 @@ public class ConnectivityServiceTest { Settings.Global.putInt(cr, Settings.Global.CAPTIVE_PORTAL_MODE, mode); } - private void setMobileDataAlwaysOn(boolean enable) { + private void setAlwaysOnNetworks(boolean enable) { ContentResolver cr = mServiceContext.getContentResolver(); Settings.Global.putInt(cr, Settings.Global.MOBILE_DATA_ALWAYS_ON, enable ? 1 : 0); - mService.updateMobileDataAlwaysOn(); + mService.updateAlwaysOnNetworks(); waitForIdle(); } @@ -2797,7 +2797,7 @@ public class ConnectivityServiceTest { public void testBackgroundNetworks() throws Exception { // Create a background request. We can't do this ourselves because ConnectivityService // doesn't have an API for it. So just turn on mobile data always on. - setMobileDataAlwaysOn(true); + setAlwaysOnNetworks(true); final NetworkRequest request = new NetworkRequest.Builder().build(); final NetworkRequest fgRequest = new NetworkRequest.Builder() .addCapability(NET_CAPABILITY_FOREGROUND).build(); @@ -2995,7 +2995,7 @@ public class ConnectivityServiceTest { // Turn on mobile data always on. The factory starts looking again. testFactory.expectAddRequests(1); - setMobileDataAlwaysOn(true); + setAlwaysOnNetworks(true); testFactory.waitForNetworkRequests(2); assertTrue(testFactory.getMyStartRequested()); @@ -3015,7 +3015,7 @@ public class ConnectivityServiceTest { // Turn off mobile data always on and expect the request to disappear... testFactory.expectRemoveRequests(1); - setMobileDataAlwaysOn(false); + setAlwaysOnNetworks(false); testFactory.waitForNetworkRequests(1); // ... and cell data to be torn down. @@ -4536,4 +4536,78 @@ public class ConnectivityServiceTest { mCellNetworkAgent.disconnect(); mCm.unregisterNetworkCallback(networkCallback); } + + @Test + public void testDataActivityTracking() throws RemoteException { + final TestNetworkCallback networkCallback = new TestNetworkCallback(); + final NetworkRequest networkRequest = new NetworkRequest.Builder() + .addCapability(NET_CAPABILITY_INTERNET) + .build(); + mCm.registerNetworkCallback(networkRequest, networkCallback); + + mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR); + final LinkProperties cellLp = new LinkProperties(); + cellLp.setInterfaceName(MOBILE_IFNAME); + mCellNetworkAgent.sendLinkProperties(cellLp); + reset(mNetworkManagementService); + mCellNetworkAgent.connect(true); + networkCallback.expectAvailableThenValidatedCallbacks(mCellNetworkAgent); + verify(mNetworkManagementService, times(1)).addIdleTimer(eq(MOBILE_IFNAME), anyInt(), + eq(ConnectivityManager.TYPE_MOBILE)); + + mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI); + final LinkProperties wifiLp = new LinkProperties(); + wifiLp.setInterfaceName(WIFI_IFNAME); + mWiFiNetworkAgent.sendLinkProperties(wifiLp); + + // Network switch + reset(mNetworkManagementService); + mWiFiNetworkAgent.connect(true); + networkCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); + networkCallback.expectCallback(CallbackState.LOSING, mCellNetworkAgent); + networkCallback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mWiFiNetworkAgent); + verify(mNetworkManagementService, times(1)).addIdleTimer(eq(WIFI_IFNAME), anyInt(), + eq(ConnectivityManager.TYPE_WIFI)); + verify(mNetworkManagementService, times(1)).removeIdleTimer(eq(MOBILE_IFNAME)); + + // Disconnect wifi and switch back to cell + reset(mNetworkManagementService); + mWiFiNetworkAgent.disconnect(); + networkCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent); + assertNoCallbacks(networkCallback); + verify(mNetworkManagementService, times(1)).removeIdleTimer(eq(WIFI_IFNAME)); + verify(mNetworkManagementService, times(1)).addIdleTimer(eq(MOBILE_IFNAME), anyInt(), + eq(ConnectivityManager.TYPE_MOBILE)); + + // reconnect wifi + mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI); + wifiLp.setInterfaceName(WIFI_IFNAME); + mWiFiNetworkAgent.sendLinkProperties(wifiLp); + mWiFiNetworkAgent.connect(true); + networkCallback.expectAvailableCallbacksUnvalidated(mWiFiNetworkAgent); + networkCallback.expectCallback(CallbackState.LOSING, mCellNetworkAgent); + networkCallback.expectCapabilitiesWith(NET_CAPABILITY_VALIDATED, mWiFiNetworkAgent); + + // Disconnect cell + reset(mNetworkManagementService); + mCellNetworkAgent.disconnect(); + networkCallback.expectCallback(CallbackState.LOST, mCellNetworkAgent); + // LOST callback is triggered earlier than removing idle timer. Broadcast should also be + // sent as network being switched. Ensure rule removal for cell will not be triggered + // unexpectedly before network being removed. + waitForIdle(); + verify(mNetworkManagementService, times(0)).removeIdleTimer(eq(MOBILE_IFNAME)); + verify(mNetworkManagementService, times(1)).removeNetwork( + eq(mCellNetworkAgent.getNetwork().netId)); + + // Disconnect wifi + ConditionVariable cv = waitForConnectivityBroadcasts(1); + reset(mNetworkManagementService); + mWiFiNetworkAgent.disconnect(); + waitFor(cv); + verify(mNetworkManagementService, times(1)).removeIdleTimer(eq(WIFI_IFNAME)); + + // Clean up + mCm.unregisterNetworkCallback(networkCallback); + } } diff --git a/tests/net/java/com/android/server/connectivity/TetheringTest.java b/tests/net/java/com/android/server/connectivity/TetheringTest.java index 40d5544dccd8..a6ed9f252008 100644 --- a/tests/net/java/com/android/server/connectivity/TetheringTest.java +++ b/tests/net/java/com/android/server/connectivity/TetheringTest.java @@ -33,6 +33,7 @@ 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 android.provider.Settings.Global.TETHER_ENABLE_LEGACY_DHCP_SERVER; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; @@ -75,6 +76,8 @@ import android.net.NetworkRequest; import android.net.NetworkState; import android.net.NetworkUtils; import android.net.RouteInfo; +import android.net.dhcp.DhcpServer; +import android.net.dhcp.DhcpServingParams; import android.net.ip.IpServer; import android.net.ip.RouterAdvertisementDaemon; import android.net.util.InterfaceParams; @@ -85,6 +88,7 @@ import android.net.wifi.WifiManager; import android.os.Bundle; import android.os.Handler; import android.os.INetworkManagementService; +import android.os.Looper; import android.os.PersistableBundle; import android.os.RemoteException; import android.os.test.TestLooper; @@ -146,6 +150,7 @@ public class TetheringTest { @Mock private UpstreamNetworkMonitor mUpstreamNetworkMonitor; @Mock private IPv6TetheringCoordinator mIPv6TetheringCoordinator; @Mock private RouterAdvertisementDaemon mRouterAdvertisementDaemon; + @Mock private DhcpServer mDhcpServer; @Mock private INetd mNetd; private final MockTetheringDependencies mTetheringDependencies = @@ -240,6 +245,12 @@ public class TetheringTest { public INetd getNetdService() { return mNetd; } + + @Override + public DhcpServer makeDhcpServer(Looper looper, InterfaceParams iface, + DhcpServingParams params, SharedLog log) { + return mDhcpServer; + } }; } @@ -333,6 +344,7 @@ public class TetheringTest { mServiceContext = new MockContext(mContext); mContentResolver = new MockContentResolver(mServiceContext); mContentResolver.addProvider(Settings.AUTHORITY, new FakeSettingsProvider()); + Settings.Global.putInt(mContentResolver, TETHER_ENABLE_LEGACY_DHCP_SERVER, 0); mIntents = new Vector<>(); mBroadcastReceiver = new BroadcastReceiver() { @Override @@ -343,12 +355,16 @@ public class TetheringTest { mServiceContext.registerReceiver(mBroadcastReceiver, new IntentFilter(ACTION_TETHER_STATE_CHANGED)); mTetheringDependencies.reset(); - mTethering = new Tethering(mServiceContext, mNMService, mStatsService, mPolicyManager, - mLooper.getLooper(), mSystemProperties, - mTetheringDependencies); + mTethering = makeTethering(); verify(mNMService).registerTetheringStatsProvider(any(), anyString()); } + private Tethering makeTethering() { + return new Tethering(mServiceContext, mNMService, mStatsService, mPolicyManager, + mLooper.getLooper(), mSystemProperties, + mTetheringDependencies); + } + @After public void tearDown() { mServiceContext.unregisterReceiver(mBroadcastReceiver); @@ -597,6 +613,18 @@ public class TetheringTest { sendIPv6TetherUpdates(upstreamState); verify(mRouterAdvertisementDaemon, never()).buildNewRa(any(), notNull()); + verify(mDhcpServer, times(1)).start(); + } + + @Test + public void workingMobileUsbTethering_IPv4LegacyDhcp() { + Settings.Global.putInt(mContentResolver, TETHER_ENABLE_LEGACY_DHCP_SERVER, 1); + mTethering = makeTethering(); + final NetworkState upstreamState = buildMobileIPv4UpstreamState(); + runUsbTethering(upstreamState); + sendIPv6TetherUpdates(upstreamState); + + verify(mDhcpServer, never()).start(); } @Test @@ -620,6 +648,7 @@ public class TetheringTest { 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(); + verify(mDhcpServer, times(1)).start(); sendIPv6TetherUpdates(upstreamState); verify(mRouterAdvertisementDaemon, times(1)).buildNewRa(any(), notNull()); @@ -633,6 +662,7 @@ public class TetheringTest { verify(mNMService, times(1)).enableNat(TEST_USB_IFNAME, TEST_XLAT_MOBILE_IFNAME); verify(mNMService, times(1)).enableNat(TEST_USB_IFNAME, TEST_MOBILE_IFNAME); + verify(mDhcpServer, times(1)).start(); verify(mNMService, times(1)).startInterfaceForwarding(TEST_USB_IFNAME, TEST_MOBILE_IFNAME); verify(mNMService, times(1)).startInterfaceForwarding(TEST_USB_IFNAME, TEST_XLAT_MOBILE_IFNAME); @@ -649,6 +679,7 @@ public class TetheringTest { runUsbTethering(upstreamState); verify(mNMService, times(1)).enableNat(TEST_USB_IFNAME, TEST_MOBILE_IFNAME); + verify(mDhcpServer, times(1)).start(); verify(mNMService, times(1)).startInterfaceForwarding(TEST_USB_IFNAME, TEST_MOBILE_IFNAME); // Then 464xlat comes up @@ -671,6 +702,8 @@ public class TetheringTest { // Forwarding was not re-added for v6 (still times(1)) verify(mNMService, times(1)).enableNat(TEST_USB_IFNAME, TEST_MOBILE_IFNAME); verify(mNMService, times(1)).startInterfaceForwarding(TEST_USB_IFNAME, TEST_MOBILE_IFNAME); + // DHCP not restarted on downstream (still times(1)) + verify(mDhcpServer, times(1)).start(); } @Test diff --git a/tests/net/java/com/android/server/connectivity/tethering/TetheringConfigurationTest.java b/tests/net/java/com/android/server/connectivity/tethering/TetheringConfigurationTest.java index bb312309b34f..521778484d91 100644 --- a/tests/net/java/com/android/server/connectivity/tethering/TetheringConfigurationTest.java +++ b/tests/net/java/com/android/server/connectivity/tethering/TetheringConfigurationTest.java @@ -225,13 +225,4 @@ public class TetheringConfigurationTest { final TetheringConfiguration cfg = new TetheringConfiguration(mMockContext, mLog); assertFalse(cfg.enableLegacyDhcpServer); } - - @Test - public void testNewDhcpServerDefault() { - Settings.Global.putString(mContentResolver, TETHER_ENABLE_LEGACY_DHCP_SERVER, null); - - final TetheringConfiguration cfg = new TetheringConfiguration(mMockContext, mLog); - // TODO: change to false when new server is promoted to default - assertTrue(cfg.enableLegacyDhcpServer); - } } diff --git a/tools/aapt2/ConfigDescription.h b/tools/aapt2/ConfigDescription.h index f71955247d78..b46a50398217 100644 --- a/tools/aapt2/ConfigDescription.h +++ b/tools/aapt2/ConfigDescription.h @@ -53,11 +53,11 @@ struct ConfigDescription : public android::ResTable_config { ConfigDescription(); ConfigDescription(const android::ResTable_config& o); // NOLINT(implicit) ConfigDescription(const ConfigDescription& o); - ConfigDescription(ConfigDescription&& o); + ConfigDescription(ConfigDescription&& o) noexcept; ConfigDescription& operator=(const android::ResTable_config& o); ConfigDescription& operator=(const ConfigDescription& o); - ConfigDescription& operator=(ConfigDescription&& o); + ConfigDescription& operator=(ConfigDescription&& o) noexcept; ConfigDescription CopyWithoutSdkVersion() const; @@ -124,7 +124,7 @@ inline ConfigDescription::ConfigDescription(const ConfigDescription& o) { *static_cast<android::ResTable_config*>(this) = o; } -inline ConfigDescription::ConfigDescription(ConfigDescription&& o) { +inline ConfigDescription::ConfigDescription(ConfigDescription&& o) noexcept { *this = o; } @@ -141,7 +141,7 @@ inline ConfigDescription& ConfigDescription::operator=( return *this; } -inline ConfigDescription& ConfigDescription::operator=(ConfigDescription&& o) { +inline ConfigDescription& ConfigDescription::operator=(ConfigDescription&& o) noexcept { *this = o; return *this; } diff --git a/tools/aapt2/io/ZipArchive.cpp b/tools/aapt2/io/ZipArchive.cpp index 8e6d7137640a..866e96d56f97 100644 --- a/tools/aapt2/io/ZipArchive.cpp +++ b/tools/aapt2/io/ZipArchive.cpp @@ -33,6 +33,11 @@ ZipFile::ZipFile(ZipArchiveHandle handle, const ZipEntry& entry, : zip_handle_(handle), zip_entry_(entry), source_(source) {} std::unique_ptr<IData> ZipFile::OpenAsData() { + // The file will fail to be mmaped if it is empty + if (zip_entry_.uncompressed_length == 0) { + return util::make_unique<EmptyData>(); + } + if (zip_entry_.method == kCompressStored) { int fd = GetFileDescriptor(zip_handle_); diff --git a/tools/aapt2/java/ManifestClassGenerator.cpp b/tools/aapt2/java/ManifestClassGenerator.cpp index be67c9c8c03c..10e504ec0752 100644 --- a/tools/aapt2/java/ManifestClassGenerator.cpp +++ b/tools/aapt2/java/ManifestClassGenerator.cpp @@ -26,21 +26,20 @@ #include "util/Maybe.h" #include "xml/XmlDom.h" -using ::android::StringPiece; using ::aapt::text::IsJavaIdentifier; namespace aapt { -static Maybe<StringPiece> ExtractJavaIdentifier(IDiagnostics* diag, const Source& source, +static Maybe<std::string> ExtractJavaIdentifier(IDiagnostics* diag, const Source& source, const std::string& value) { - StringPiece result = value; + std::string result = value; size_t pos = value.rfind('.'); if (pos != std::string::npos) { result = result.substr(pos + 1); } // Normalize only the java identifier, leave the original value unchanged. - if (result.contains("-")) { + if (result.find("-") != std::string::npos) { result = JavaClassGenerator::TransformToFieldName(result); } @@ -64,7 +63,7 @@ static bool WriteSymbol(const Source& source, IDiagnostics* diag, xml::Element* return false; } - Maybe<StringPiece> result = + Maybe<std::string> result = ExtractJavaIdentifier(diag, source.WithLine(el->line_number), attr->value); if (!result) { return false; diff --git a/tools/aapt2/util/BigBuffer.h b/tools/aapt2/util/BigBuffer.h index 30452552888e..d4b3abce68a7 100644 --- a/tools/aapt2/util/BigBuffer.h +++ b/tools/aapt2/util/BigBuffer.h @@ -68,7 +68,7 @@ class BigBuffer { */ explicit BigBuffer(size_t block_size); - BigBuffer(BigBuffer&& rhs); + BigBuffer(BigBuffer&& rhs) noexcept; /** * Number of occupied bytes in all the allocated blocks. @@ -136,7 +136,7 @@ class BigBuffer { inline BigBuffer::BigBuffer(size_t block_size) : block_size_(block_size), size_(0) {} -inline BigBuffer::BigBuffer(BigBuffer&& rhs) +inline BigBuffer::BigBuffer(BigBuffer&& rhs) noexcept : block_size_(rhs.block_size_), size_(rhs.size_), blocks_(std::move(rhs.blocks_)) {} diff --git a/tools/aapt2/util/ImmutableMap.h b/tools/aapt2/util/ImmutableMap.h index 59858e492c4c..1727b18e4106 100644 --- a/tools/aapt2/util/ImmutableMap.h +++ b/tools/aapt2/util/ImmutableMap.h @@ -32,8 +32,8 @@ class ImmutableMap { using const_iterator = typename std::vector<std::pair<TKey, TValue>>::const_iterator; - ImmutableMap(ImmutableMap&&) = default; - ImmutableMap& operator=(ImmutableMap&&) = default; + ImmutableMap(ImmutableMap&&) noexcept = default; + ImmutableMap& operator=(ImmutableMap&&) noexcept = default; static ImmutableMap<TKey, TValue> CreatePreSorted( std::initializer_list<std::pair<TKey, TValue>> list) { diff --git a/tools/aapt2/util/Maybe.h b/tools/aapt2/util/Maybe.h index 9a82418e0a5a..031276c8b885 100644 --- a/tools/aapt2/util/Maybe.h +++ b/tools/aapt2/util/Maybe.h @@ -46,7 +46,7 @@ class Maybe { template <typename U> Maybe(const Maybe<U>& rhs); // NOLINT(implicit) - Maybe(Maybe&& rhs); + Maybe(Maybe&& rhs) noexcept; template <typename U> Maybe(Maybe<U>&& rhs); // NOLINT(implicit) @@ -56,7 +56,7 @@ class Maybe { template <typename U> Maybe& operator=(const Maybe<U>& rhs); - Maybe& operator=(Maybe&& rhs); + Maybe& operator=(Maybe&& rhs) noexcept; template <typename U> Maybe& operator=(Maybe<U>&& rhs); @@ -134,7 +134,7 @@ Maybe<T>::Maybe(const Maybe<U>& rhs) : nothing_(rhs.nothing_) { } template <typename T> -Maybe<T>::Maybe(Maybe&& rhs) : nothing_(rhs.nothing_) { +Maybe<T>::Maybe(Maybe&& rhs) noexcept : nothing_(rhs.nothing_) { if (!rhs.nothing_) { rhs.nothing_ = true; @@ -192,7 +192,7 @@ Maybe<T>& Maybe<T>::copy(const Maybe<U>& rhs) { } template <typename T> -inline Maybe<T>& Maybe<T>::operator=(Maybe&& rhs) { +inline Maybe<T>& Maybe<T>::operator=(Maybe&& rhs) noexcept { // Delegate to the actual assignment. return move(std::forward<Maybe<T>>(rhs)); } diff --git a/tools/fonts/add_additional_fonts.py b/tools/fonts/add_additional_fonts.py deleted file mode 100644 index bf4af2b1c56e..000000000000 --- a/tools/fonts/add_additional_fonts.py +++ /dev/null @@ -1,44 +0,0 @@ -#!/usr/bin/env python -# -# 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. -# - -import sys - -def main(argv): - original_file = 'frameworks/base/data/fonts/fonts.xml' - - if len(argv) == 3: - output_file_path = argv[1] - override_file_path = argv[2] - else: - raise ValueError("Wrong number of arguments %s" % len(argv)) - - fallbackPlaceholderFound = False - with open(original_file, 'r') as input_file: - with open(output_file_path, 'w') as output_file: - for line in input_file: - # If we've found the spot to add additional fonts, add them. - if line.strip() == '<!-- fallback fonts -->': - fallbackPlaceholderFound = True - with open(override_file_path) as override_file: - for override_line in override_file: - output_file.write(override_line) - output_file.write(line) - if not fallbackPlaceholderFound: - raise ValueError('<!-- fallback fonts --> not found in source file: %s' % original_file) - -if __name__ == '__main__': - main(sys.argv) |