diff options
264 files changed, 7022 insertions, 3482 deletions
diff --git a/Android.bp b/Android.bp index b64907adb60a..05474815f62c 100644 --- a/Android.bp +++ b/Android.bp @@ -414,6 +414,7 @@ java_library { "libcore-platform-compat-config", "services-platform-compat-config", "media-provider-platform-compat-config", + "services-devicepolicy-platform-compat-config", ], sdk_version: "core_platform", } @@ -1595,4 +1596,4 @@ filegroup { "core/java/com/android/internal/util/State.java", "core/java/com/android/internal/util/StateMachine.java", ], -}
\ No newline at end of file +} diff --git a/api/current.txt b/api/current.txt index 603dec283c66..40106b3a5724 100644 --- a/api/current.txt +++ b/api/current.txt @@ -5872,6 +5872,7 @@ package android.app { method public android.service.notification.StatusBarNotification[] getActiveNotifications(); method public android.app.AutomaticZenRule getAutomaticZenRule(String); method public java.util.Map<java.lang.String,android.app.AutomaticZenRule> getAutomaticZenRules(); + method @Nullable public android.app.NotificationManager.Policy getConsolidatedNotificationPolicy(); method public final int getCurrentInterruptionFilter(); method public int getImportance(); method public android.app.NotificationChannel getNotificationChannel(String); @@ -24651,6 +24652,7 @@ package android.media { field public static final int DolbyVisionLevelUhd30 = 64; // 0x40 field public static final int DolbyVisionLevelUhd48 = 128; // 0x80 field public static final int DolbyVisionLevelUhd60 = 256; // 0x100 + field public static final int DolbyVisionProfileDvav110 = 1024; // 0x400 field public static final int DolbyVisionProfileDvavPen = 2; // 0x2 field public static final int DolbyVisionProfileDvavPer = 1; // 0x1 field public static final int DolbyVisionProfileDvavSe = 512; // 0x200 @@ -38280,6 +38282,7 @@ package android.provider { field public static final String COLUMN_MIME_TYPE = "mime_type"; field public static final String COLUMN_SIZE = "_size"; field public static final String COLUMN_SUMMARY = "summary"; + field public static final int FLAG_DIR_BLOCKS_TREE = 32768; // 0x8000 field public static final int FLAG_DIR_PREFERS_GRID = 16; // 0x10 field public static final int FLAG_DIR_PREFERS_LAST_MODIFIED = 32; // 0x20 field public static final int FLAG_DIR_SUPPORTS_CREATE = 8; // 0x8 @@ -38498,16 +38501,17 @@ package android.provider { public static final class MediaStore.Audio { ctor public MediaStore.Audio(); - method public static String keyFor(String); + method @Deprecated @Nullable public static String keyFor(@Nullable String); } public static interface MediaStore.Audio.AlbumColumns { field public static final String ALBUM = "album"; field @Deprecated public static final String ALBUM_ART = "album_art"; field public static final String ALBUM_ID = "album_id"; - field public static final String ALBUM_KEY = "album_key"; + field @Deprecated public static final String ALBUM_KEY = "album_key"; field public static final String ARTIST = "artist"; field public static final String ARTIST_ID = "artist_id"; + field @Deprecated public static final String ARTIST_KEY = "artist_key"; field public static final String FIRST_YEAR = "minyear"; field public static final String LAST_YEAR = "maxyear"; field public static final String NUMBER_OF_SONGS = "numsongs"; @@ -38526,7 +38530,7 @@ package android.provider { public static interface MediaStore.Audio.ArtistColumns { field public static final String ARTIST = "artist"; - field public static final String ARTIST_KEY = "artist_key"; + field @Deprecated public static final String ARTIST_KEY = "artist_key"; field public static final String NUMBER_OF_ALBUMS = "number_of_albums"; field public static final String NUMBER_OF_TRACKS = "number_of_tracks"; } @@ -38549,19 +38553,23 @@ package android.provider { public static interface MediaStore.Audio.AudioColumns extends android.provider.MediaStore.MediaColumns { field public static final String ALBUM = "album"; field public static final String ALBUM_ID = "album_id"; - field public static final String ALBUM_KEY = "album_key"; + field @Deprecated public static final String ALBUM_KEY = "album_key"; field public static final String ARTIST = "artist"; field public static final String ARTIST_ID = "artist_id"; - field public static final String ARTIST_KEY = "artist_key"; + field @Deprecated public static final String ARTIST_KEY = "artist_key"; field public static final String BOOKMARK = "bookmark"; field public static final String COMPOSER = "composer"; + field public static final String GENRE = "genre"; + field public static final String GENRE_ID = "genre_id"; + field @Deprecated public static final String GENRE_KEY = "genre_key"; field public static final String IS_ALARM = "is_alarm"; field public static final String IS_AUDIOBOOK = "is_audiobook"; field public static final String IS_MUSIC = "is_music"; field public static final String IS_NOTIFICATION = "is_notification"; field public static final String IS_PODCAST = "is_podcast"; field public static final String IS_RINGTONE = "is_ringtone"; - field public static final String TITLE_KEY = "title_key"; + field @Deprecated public static final String TITLE_KEY = "title_key"; + field public static final String TITLE_RESOURCE_URI = "title_resource_uri"; field public static final String TRACK = "track"; field public static final String YEAR = "year"; } @@ -44250,6 +44258,7 @@ package android.telephony { field public static final String KEY_DEFAULT_VM_NUMBER_STRING = "default_vm_number_string"; field public static final String KEY_DIAL_STRING_REPLACE_STRING_ARRAY = "dial_string_replace_string_array"; field public static final String KEY_DISABLE_CDMA_ACTIVATION_CODE_BOOL = "disable_cdma_activation_code_bool"; + field public static final String KEY_DISCONNECT_CAUSE_PLAY_BUSYTONE_INT_ARRAY = "disconnect_cause_play_busytone_int_array"; field public static final String KEY_DISPLAY_HD_AUDIO_PROPERTY_BOOL = "display_hd_audio_property_bool"; field public static final String KEY_DROP_VIDEO_CALL_WHEN_ANSWERING_AUDIO_CALL_BOOL = "drop_video_call_when_answering_audio_call_bool"; field public static final String KEY_DTMF_TYPE_ENABLED_BOOL = "dtmf_type_enabled_bool"; @@ -44595,6 +44604,7 @@ package android.telephony { method public int describeContents(); method public int getAsuLevel(); method public int getDbm(); + method public int getEcNo(); method @IntRange(from=android.telephony.CellSignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN, to=android.telephony.CellSignalStrength.SIGNAL_STRENGTH_GREAT) public int getLevel(); method public void writeToParcel(android.os.Parcel, int); field @NonNull public static final android.os.Parcelable.Creator<android.telephony.CellSignalStrengthWcdma> CREATOR; @@ -45100,6 +45110,7 @@ package android.telephony { method public long getDataLimitBytes(); method public long getDataUsageBytes(); method public long getDataUsageTime(); + method @Nullable public int[] getNetworkTypes(); method @Nullable public CharSequence getSummary(); method @Nullable public CharSequence getTitle(); method public void writeToParcel(android.os.Parcel, int); @@ -45119,6 +45130,7 @@ package android.telephony { method public static android.telephony.SubscriptionPlan.Builder createRecurring(java.time.ZonedDateTime, java.time.Period); method public android.telephony.SubscriptionPlan.Builder setDataLimit(long, int); method public android.telephony.SubscriptionPlan.Builder setDataUsage(long, long); + method @NonNull public android.telephony.SubscriptionPlan.Builder setNetworkTypes(@Nullable int[]); method public android.telephony.SubscriptionPlan.Builder setSummary(@Nullable CharSequence); method public android.telephony.SubscriptionPlan.Builder setTitle(@Nullable CharSequence); } diff --git a/api/system-current.txt b/api/system-current.txt index a0071ad79492..46413d39caca 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -1359,7 +1359,7 @@ package android.content { public abstract class Context { method @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public boolean bindServiceAsUser(@RequiresPermission android.content.Intent, android.content.ServiceConnection, int, android.os.UserHandle); - method @NonNull public android.content.Context createContextAsUser(@NonNull android.os.UserHandle); + method @NonNull public android.content.Context createContextAsUser(@NonNull android.os.UserHandle, int); method public abstract android.content.Context createCredentialProtectedStorageContext(); method @NonNull public android.content.Context createPackageContextAsUser(@NonNull String, int, @NonNull android.os.UserHandle) throws android.content.pm.PackageManager.NameNotFoundException; method @Nullable public abstract java.io.File getPreloadsFileCache(); @@ -1756,6 +1756,7 @@ package android.content.pm { field public static final int PROTECTION_FLAG_SYSTEM_TEXT_CLASSIFIER = 65536; // 0x10000 field public static final int PROTECTION_FLAG_TELEPHONY = 4194304; // 0x400000 field public static final int PROTECTION_FLAG_WELLBEING = 131072; // 0x20000 + field public static final int PROTECTION_FLAG_WIFI = 8388608; // 0x800000 field @Nullable public final String backgroundPermission; field @StringRes public int requestRes; } @@ -7315,7 +7316,7 @@ package android.telephony { public abstract class CellBroadcastService extends android.app.Service { ctor public CellBroadcastService(); method @CallSuper public android.os.IBinder onBind(android.content.Intent); - method public abstract void onCdmaCellBroadcastSms(int, byte[]); + method public abstract void onCdmaCellBroadcastSms(int, byte[], int); method public abstract void onGsmCellBroadcastSms(int, byte[]); field public static final String CELL_BROADCAST_SERVICE_INTERFACE = "android.telephony.CellBroadcastService"; } @@ -9761,9 +9762,10 @@ package android.view { method public final long getUserActivityTimeout(); method public final void setUserActivityTimeout(long); field @RequiresPermission(android.Manifest.permission.HIDE_NON_SYSTEM_OVERLAY_WINDOWS) public static final int SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS = 524288; // 0x80000 + field @RequiresPermission(android.Manifest.permission.INTERNAL_SYSTEM_WINDOW) public static final int SYSTEM_FLAG_SHOW_FOR_ALL_USERS = 16; // 0x10 } - @IntDef(flag=true, prefix={"SYSTEM_FLAG_"}, value={android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS}) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface WindowManager.LayoutParams.SystemFlags { + @IntDef(flag=true, prefix={"SYSTEM_FLAG_"}, value={android.view.WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS, android.view.WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS}) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) public static @interface WindowManager.LayoutParams.SystemFlags { } } diff --git a/api/test-current.txt b/api/test-current.txt index 2ed88adf8d87..9b85b96da5ab 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -659,7 +659,7 @@ package android.content { } public abstract class Context { - method @NonNull public android.content.Context createContextAsUser(@NonNull android.os.UserHandle); + method @NonNull public android.content.Context createContextAsUser(@NonNull android.os.UserHandle, int); method @NonNull public android.content.Context createPackageContextAsUser(@NonNull String, int, @NonNull android.os.UserHandle) throws android.content.pm.PackageManager.NameNotFoundException; method public abstract android.view.Display getDisplay(); method public abstract int getDisplayId(); @@ -773,6 +773,7 @@ package android.content.pm { field public static final int PROTECTION_FLAG_TELEPHONY = 4194304; // 0x400000 field public static final int PROTECTION_FLAG_VENDOR_PRIVILEGED = 32768; // 0x8000 field public static final int PROTECTION_FLAG_WELLBEING = 131072; // 0x20000 + field public static final int PROTECTION_FLAG_WIFI = 8388608; // 0x800000 field @Nullable public final String backgroundPermission; } diff --git a/cmds/statsd/Android.bp b/cmds/statsd/Android.bp index cb2732561b56..8af925aa7a63 100644 --- a/cmds/statsd/Android.bp +++ b/cmds/statsd/Android.bp @@ -230,6 +230,7 @@ cc_test { "tests/e2e/Anomaly_duration_sum_e2e_test.cpp", "tests/e2e/Attribution_e2e_test.cpp", "tests/e2e/ConfigTtl_e2e_test.cpp", + "tests/e2e/CountMetric_e2e_test.cpp", "tests/e2e/DurationMetric_e2e_test.cpp", "tests/e2e/GaugeMetric_e2e_pull_test.cpp", "tests/e2e/GaugeMetric_e2e_push_test.cpp", diff --git a/cmds/statsd/src/StatsLogProcessor.cpp b/cmds/statsd/src/StatsLogProcessor.cpp index ff7416c4b9e0..6c3dff24e5fe 100644 --- a/cmds/statsd/src/StatsLogProcessor.cpp +++ b/cmds/statsd/src/StatsLogProcessor.cpp @@ -16,24 +16,26 @@ #define DEBUG false // STOPSHIP if true #include "Log.h" -#include "statslog.h" + +#include "StatsLogProcessor.h" #include <android-base/file.h> #include <dirent.h> #include <frameworks/base/cmds/statsd/src/active_config_list.pb.h> -#include "StatsLogProcessor.h" +#include <log/log_event_list.h> +#include <utils/Errors.h> +#include <utils/SystemClock.h> + #include "android-base/stringprintf.h" #include "external/StatsPullerManager.h" #include "guardrail/StatsdStats.h" #include "metrics/CountMetricProducer.h" +#include "state/StateManager.h" #include "stats_log_util.h" #include "stats_util.h" +#include "statslog.h" #include "storage/StorageManager.h" -#include <log/log_event_list.h> -#include <utils/Errors.h> -#include <utils/SystemClock.h> - using namespace android; using android::base::StringPrintf; using android::util::FIELD_COUNT_REPEATED; @@ -218,6 +220,8 @@ void StatsLogProcessor::OnLogEvent(LogEvent* event) { onIsolatedUidChangedEventLocked(*event); } + StateManager::getInstance().onLogEvent(*event); + if (mMetricsManagers.empty()) { return; } diff --git a/cmds/statsd/src/StatsLogProcessor.h b/cmds/statsd/src/StatsLogProcessor.h index 3d002d2efdd0..8292a3a9194a 100644 --- a/cmds/statsd/src/StatsLogProcessor.h +++ b/cmds/statsd/src/StatsLogProcessor.h @@ -262,6 +262,10 @@ private: FRIEND_TEST(MetricActivationE2eTest, TestCountMetricWithSameDeactivation); FRIEND_TEST(MetricActivationE2eTest, TestCountMetricWithTwoMetricsTwoDeactivations); + FRIEND_TEST(CountMetricE2eTest, TestWithSimpleState); + FRIEND_TEST(CountMetricE2eTest, TestWithMappedState); + FRIEND_TEST(CountMetricE2eTest, TestWithMultipleStates); + FRIEND_TEST(DurationMetricE2eTest, TestOneBucket); FRIEND_TEST(DurationMetricE2eTest, TestTwoBuckets); FRIEND_TEST(DurationMetricE2eTest, TestWithActivation); diff --git a/cmds/statsd/src/anomaly/subscriber_util.cpp b/cmds/statsd/src/anomaly/subscriber_util.cpp index e09d5751d323..4c30c4cb223c 100644 --- a/cmds/statsd/src/anomaly/subscriber_util.cpp +++ b/cmds/statsd/src/anomaly/subscriber_util.cpp @@ -40,7 +40,7 @@ void triggerSubscribers(int64_t ruleId, int64_t metricId, const MetricDimensionK for (const Subscription& subscription : subscriptions) { if (subscription.probability_of_informing() < 1 - && ((float)rand() / RAND_MAX) >= subscription.probability_of_informing()) { + && ((float)rand() / (float)RAND_MAX) >= subscription.probability_of_informing()) { // Note that due to float imprecision, 0.0 and 1.0 might not truly mean never/always. // The config writer was advised to use -0.1 and 1.1 for never/always. ALOGI("Fate decided that a subscriber would not be informed."); diff --git a/cmds/statsd/src/metrics/CountMetricProducer.cpp b/cmds/statsd/src/metrics/CountMetricProducer.cpp index b5c8e35accad..4a06387e357f 100644 --- a/cmds/statsd/src/metrics/CountMetricProducer.cpp +++ b/cmds/statsd/src/metrics/CountMetricProducer.cpp @@ -18,13 +18,15 @@ #include "Log.h" #include "CountMetricProducer.h" -#include "guardrail/StatsdStats.h" -#include "stats_util.h" -#include "stats_log_util.h" +#include <inttypes.h> #include <limits.h> #include <stdlib.h> +#include "guardrail/StatsdStats.h" +#include "stats_log_util.h" +#include "stats_util.h" + using android::util::FIELD_COUNT_REPEATED; using android::util::FIELD_TYPE_BOOL; using android::util::FIELD_TYPE_FLOAT; @@ -65,16 +67,16 @@ const int FIELD_ID_BUCKET_NUM = 4; const int FIELD_ID_START_BUCKET_ELAPSED_MILLIS = 5; const int FIELD_ID_END_BUCKET_ELAPSED_MILLIS = 6; -CountMetricProducer::CountMetricProducer(const ConfigKey& key, const CountMetric& metric, - const int conditionIndex, - const sp<ConditionWizard>& wizard, - const int64_t timeBaseNs, const int64_t startTimeNs, - const unordered_map<int, shared_ptr<Activation>>& - eventActivationMap, - const unordered_map<int, vector<shared_ptr<Activation>>>& - eventDeactivationMap) +CountMetricProducer::CountMetricProducer( + const ConfigKey& key, const CountMetric& metric, const int conditionIndex, + const sp<ConditionWizard>& wizard, const int64_t timeBaseNs, const int64_t startTimeNs, + + const unordered_map<int, shared_ptr<Activation>>& eventActivationMap, + const unordered_map<int, vector<shared_ptr<Activation>>>& eventDeactivationMap, + const vector<int>& slicedStateAtoms, + const unordered_map<int, unordered_map<int, int64_t>>& stateGroupMap) : MetricProducer(metric.id(), key, timeBaseNs, conditionIndex, wizard, eventActivationMap, - eventDeactivationMap) { + eventDeactivationMap, slicedStateAtoms, stateGroupMap) { if (metric.has_bucket()) { mBucketSizeNs = TimeUnitToBucketSizeInMillisGuardrailed(key.GetUid(), metric.bucket()) * 1000000; @@ -100,6 +102,8 @@ CountMetricProducer::CountMetricProducer(const ConfigKey& key, const CountMetric mConditionSliced = true; } + // TODO(tsaichristine): b/142124705 handle metric state links + flushIfNeededLocked(startTimeNs); // Adjust start for partial bucket mCurrentBucketStartTimeNs = startTimeNs; @@ -112,6 +116,12 @@ CountMetricProducer::~CountMetricProducer() { VLOG("~CountMetricProducer() called"); } +void CountMetricProducer::onStateChanged(int atomId, const HashableDimensionKey& primaryKey, + int oldState, int newState) { + VLOG("CountMetric %lld onStateChanged State%d, key %s, %d -> %d", (long long)mMetricId, atomId, + primaryKey.toString().c_str(), oldState, newState); +} + void CountMetricProducer::dumpStatesLocked(FILE* out, bool verbose) const { if (mCurrentSlicedCounter == nullptr || mCurrentSlicedCounter->size() == 0) { diff --git a/cmds/statsd/src/metrics/CountMetricProducer.h b/cmds/statsd/src/metrics/CountMetricProducer.h index 61913c71cdcc..61e0892d50a9 100644 --- a/cmds/statsd/src/metrics/CountMetricProducer.h +++ b/cmds/statsd/src/metrics/CountMetricProducer.h @@ -17,15 +17,16 @@ #ifndef COUNT_METRIC_PRODUCER_H #define COUNT_METRIC_PRODUCER_H -#include <unordered_map> - #include <android/util/ProtoOutputStream.h> #include <gtest/gtest_prod.h> -#include "../anomaly/AnomalyTracker.h" -#include "../condition/ConditionTracker.h" -#include "../matchers/matcher_util.h" + +#include <unordered_map> + #include "MetricProducer.h" +#include "anomaly/AnomalyTracker.h" +#include "condition/ConditionTracker.h" #include "frameworks/base/cmds/statsd/src/statsd_config.pb.h" +#include "matchers/matcher_util.h" #include "stats_util.h" namespace android { @@ -40,16 +41,20 @@ struct CountBucket { class CountMetricProducer : public MetricProducer { public: - CountMetricProducer(const ConfigKey& key, const CountMetric& countMetric, - const int conditionIndex, const sp<ConditionWizard>& wizard, - const int64_t timeBaseNs, const int64_t startTimeNs, - const std::unordered_map<int, std::shared_ptr<Activation>>& - eventActivationMap = {}, - const std::unordered_map<int, std::vector<std::shared_ptr<Activation>>>& - eventDeactivationMap = {}); + CountMetricProducer( + const ConfigKey& key, const CountMetric& countMetric, const int conditionIndex, + const sp<ConditionWizard>& wizard, const int64_t timeBaseNs, const int64_t startTimeNs, + const std::unordered_map<int, std::shared_ptr<Activation>>& eventActivationMap = {}, + const std::unordered_map<int, std::vector<std::shared_ptr<Activation>>>& + eventDeactivationMap = {}, + const vector<int>& slicedStateAtoms = {}, + const unordered_map<int, unordered_map<int, int64_t>>& stateGroupMap = {}); virtual ~CountMetricProducer(); + void onStateChanged(int atomId, const HashableDimensionKey& primaryKey, int oldState, + int newState) override; + protected: void onMatchedLogEventInternalLocked( const size_t matcherIndex, const MetricDimensionKey& eventKey, @@ -106,6 +111,7 @@ private: FRIEND_TEST(CountMetricProducerTest, TestEventWithAppUpgrade); FRIEND_TEST(CountMetricProducerTest, TestEventWithAppUpgradeInNextBucket); FRIEND_TEST(CountMetricProducerTest, TestFirstBucket); + FRIEND_TEST(CountMetricProducerTest, TestOneWeekTimeUnit); }; } // namespace statsd diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.cpp b/cmds/statsd/src/metrics/DurationMetricProducer.cpp index 31b90f3b13b1..ab2a1c3bd65c 100644 --- a/cmds/statsd/src/metrics/DurationMetricProducer.cpp +++ b/cmds/statsd/src/metrics/DurationMetricProducer.cpp @@ -69,9 +69,11 @@ DurationMetricProducer::DurationMetricProducer( const bool nesting, const sp<ConditionWizard>& wizard, const FieldMatcher& internalDimensions, const int64_t timeBaseNs, const int64_t startTimeNs, const unordered_map<int, shared_ptr<Activation>>& eventActivationMap, - const unordered_map<int, vector<shared_ptr<Activation>>>& eventDeactivationMap) + const unordered_map<int, vector<shared_ptr<Activation>>>& eventDeactivationMap, + const vector<int>& slicedStateAtoms, + const unordered_map<int, unordered_map<int, int64_t>>& stateGroupMap) : MetricProducer(metric.id(), key, timeBaseNs, conditionIndex, wizard, eventActivationMap, - eventDeactivationMap), + eventDeactivationMap, slicedStateAtoms, stateGroupMap), mAggregationType(metric.aggregation_type()), mStartIndex(startIndex), mStopIndex(stopIndex), diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.h b/cmds/statsd/src/metrics/DurationMetricProducer.h index 0592b1808f52..7457d7fb2dd9 100644 --- a/cmds/statsd/src/metrics/DurationMetricProducer.h +++ b/cmds/statsd/src/metrics/DurationMetricProducer.h @@ -38,16 +38,16 @@ namespace statsd { class DurationMetricProducer : public MetricProducer { public: - DurationMetricProducer(const ConfigKey& key, const DurationMetric& durationMetric, - const int conditionIndex, const size_t startIndex, - const size_t stopIndex, const size_t stopAllIndex, const bool nesting, - const sp<ConditionWizard>& wizard, - const FieldMatcher& internalDimensions, const int64_t timeBaseNs, - const int64_t startTimeNs, - const unordered_map<int, shared_ptr<Activation>>& - eventActivationMap = {}, - const unordered_map<int, vector<shared_ptr<Activation>>>& - eventDeactivationMap = {}); + DurationMetricProducer( + const ConfigKey& key, const DurationMetric& durationMetric, const int conditionIndex, + const size_t startIndex, const size_t stopIndex, const size_t stopAllIndex, + const bool nesting, const sp<ConditionWizard>& wizard, + const FieldMatcher& internalDimensions, const int64_t timeBaseNs, + const int64_t startTimeNs, + const unordered_map<int, shared_ptr<Activation>>& eventActivationMap = {}, + const unordered_map<int, vector<shared_ptr<Activation>>>& eventDeactivationMap = {}, + const vector<int>& slicedStateAtoms = {}, + const unordered_map<int, unordered_map<int, int64_t>>& stateGroupMap = {}); virtual ~DurationMetricProducer(); diff --git a/cmds/statsd/src/metrics/EventMetricProducer.cpp b/cmds/statsd/src/metrics/EventMetricProducer.cpp index a60a9161bdbb..32eb077e1cf4 100644 --- a/cmds/statsd/src/metrics/EventMetricProducer.cpp +++ b/cmds/statsd/src/metrics/EventMetricProducer.cpp @@ -52,16 +52,15 @@ const int FIELD_ID_DATA = 1; const int FIELD_ID_ELAPSED_TIMESTAMP_NANOS = 1; const int FIELD_ID_ATOMS = 2; -EventMetricProducer::EventMetricProducer(const ConfigKey& key, const EventMetric& metric, - const int conditionIndex, - const sp<ConditionWizard>& wizard, - const int64_t startTimeNs, - const unordered_map<int, shared_ptr<Activation>>& - eventActivationMap, - const unordered_map<int, vector<shared_ptr<Activation>>>& - eventDeactivationMap) +EventMetricProducer::EventMetricProducer( + const ConfigKey& key, const EventMetric& metric, const int conditionIndex, + const sp<ConditionWizard>& wizard, const int64_t startTimeNs, + const unordered_map<int, shared_ptr<Activation>>& eventActivationMap, + const unordered_map<int, vector<shared_ptr<Activation>>>& eventDeactivationMap, + const vector<int>& slicedStateAtoms, + const unordered_map<int, unordered_map<int, int64_t>>& stateGroupMap) : MetricProducer(metric.id(), key, startTimeNs, conditionIndex, wizard, eventActivationMap, - eventDeactivationMap) { + eventDeactivationMap, slicedStateAtoms, stateGroupMap) { if (metric.links().size() > 0) { for (const auto& link : metric.links()) { Metric2Condition mc; diff --git a/cmds/statsd/src/metrics/EventMetricProducer.h b/cmds/statsd/src/metrics/EventMetricProducer.h index aab53c8b6816..dca37e8790a4 100644 --- a/cmds/statsd/src/metrics/EventMetricProducer.h +++ b/cmds/statsd/src/metrics/EventMetricProducer.h @@ -33,13 +33,14 @@ namespace statsd { class EventMetricProducer : public MetricProducer { public: - EventMetricProducer(const ConfigKey& key, const EventMetric& eventMetric, - const int conditionIndex, const sp<ConditionWizard>& wizard, - const int64_t startTimeNs, - const std::unordered_map<int, std::shared_ptr<Activation>>& - eventActivationMap = {}, - const std::unordered_map<int, std::vector<std::shared_ptr<Activation>>>& - eventDeactivationMap = {}); + EventMetricProducer( + const ConfigKey& key, const EventMetric& eventMetric, const int conditionIndex, + const sp<ConditionWizard>& wizard, const int64_t startTimeNs, + const std::unordered_map<int, std::shared_ptr<Activation>>& eventActivationMap = {}, + const std::unordered_map<int, std::vector<std::shared_ptr<Activation>>>& + eventDeactivationMap = {}, + const vector<int>& slicedStateAtoms = {}, + const unordered_map<int, unordered_map<int, int64_t>>& stateGroupMap = {}); virtual ~EventMetricProducer(); diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp index e409b6fbb9e9..d0f88a867da7 100644 --- a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp +++ b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp @@ -74,9 +74,11 @@ GaugeMetricProducer::GaugeMetricProducer( const int atomId, const int64_t timeBaseNs, const int64_t startTimeNs, const sp<StatsPullerManager>& pullerManager, const unordered_map<int, shared_ptr<Activation>>& eventActivationMap, - const unordered_map<int, vector<shared_ptr<Activation>>>& eventDeactivationMap) + const unordered_map<int, vector<shared_ptr<Activation>>>& eventDeactivationMap, + const vector<int>& slicedStateAtoms, + const unordered_map<int, unordered_map<int, int64_t>>& stateGroupMap) : MetricProducer(metric.id(), key, timeBaseNs, conditionIndex, wizard, eventActivationMap, - eventDeactivationMap), + eventDeactivationMap, slicedStateAtoms, stateGroupMap), mWhatMatcherIndex(whatMatcherIndex), mEventMatcherWizard(matcherWizard), mPullerManager(pullerManager), diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.h b/cmds/statsd/src/metrics/GaugeMetricProducer.h index dfe1d56c61c7..640a02a9a8ab 100644 --- a/cmds/statsd/src/metrics/GaugeMetricProducer.h +++ b/cmds/statsd/src/metrics/GaugeMetricProducer.h @@ -56,16 +56,17 @@ typedef std::unordered_map<MetricDimensionKey, std::vector<GaugeAtom>> // producer always reports the guage at the earliest time of the bucket when the condition is met. class GaugeMetricProducer : public virtual MetricProducer, public virtual PullDataReceiver { public: - GaugeMetricProducer(const ConfigKey& key, const GaugeMetric& gaugeMetric, - const int conditionIndex, const sp<ConditionWizard>& conditionWizard, - const int whatMatcherIndex,const sp<EventMatcherWizard>& matcherWizard, - const int pullTagId, const int triggerAtomId, const int atomId, - const int64_t timeBaseNs, const int64_t startTimeNs, - const sp<StatsPullerManager>& pullerManager, - const std::unordered_map<int, std::shared_ptr<Activation>>& - eventActivationMap = {}, - const std::unordered_map<int, std::vector<std::shared_ptr<Activation>>>& - eventDeactivationMap = {}); + GaugeMetricProducer( + const ConfigKey& key, const GaugeMetric& gaugeMetric, const int conditionIndex, + const sp<ConditionWizard>& conditionWizard, const int whatMatcherIndex, + const sp<EventMatcherWizard>& matcherWizard, const int pullTagId, + const int triggerAtomId, const int atomId, const int64_t timeBaseNs, + const int64_t startTimeNs, const sp<StatsPullerManager>& pullerManager, + const std::unordered_map<int, std::shared_ptr<Activation>>& eventActivationMap = {}, + const std::unordered_map<int, std::vector<std::shared_ptr<Activation>>>& + eventDeactivationMap = {}, + const vector<int>& slicedStateAtoms = {}, + const unordered_map<int, unordered_map<int, int64_t>>& stateGroupMap = {}); virtual ~GaugeMetricProducer(); diff --git a/cmds/statsd/src/metrics/MetricProducer.cpp b/cmds/statsd/src/metrics/MetricProducer.cpp index 3426a195a748..2a700efe3d37 100644 --- a/cmds/statsd/src/metrics/MetricProducer.cpp +++ b/cmds/statsd/src/metrics/MetricProducer.cpp @@ -45,23 +45,27 @@ MetricProducer::MetricProducer( const int conditionIndex, const sp<ConditionWizard>& wizard, const std::unordered_map<int, std::shared_ptr<Activation>>& eventActivationMap, const std::unordered_map<int, std::vector<std::shared_ptr<Activation>>>& - eventDeactivationMap) - : mMetricId(metricId), - mConfigKey(key), - mTimeBaseNs(timeBaseNs), - mCurrentBucketStartTimeNs(timeBaseNs), - mCurrentBucketNum(0), - mCondition(initialCondition(conditionIndex)), - mConditionTrackerIndex(conditionIndex), - mConditionSliced(false), - mWizard(wizard), - mContainANYPositionInDimensionsInWhat(false), - mSliceByPositionALL(false), - mHasLinksToAllConditionDimensionsInTracker(false), - mEventActivationMap(eventActivationMap), - mEventDeactivationMap(eventDeactivationMap), - mIsActive(mEventActivationMap.empty()) { - } + eventDeactivationMap, + const vector<int>& slicedStateAtoms, + const unordered_map<int, unordered_map<int, int64_t>>& stateGroupMap) + : mMetricId(metricId), + mConfigKey(key), + mTimeBaseNs(timeBaseNs), + mCurrentBucketStartTimeNs(timeBaseNs), + mCurrentBucketNum(0), + mCondition(initialCondition(conditionIndex)), + mConditionTrackerIndex(conditionIndex), + mConditionSliced(false), + mWizard(wizard), + mContainANYPositionInDimensionsInWhat(false), + mSliceByPositionALL(false), + mHasLinksToAllConditionDimensionsInTracker(false), + mEventActivationMap(eventActivationMap), + mEventDeactivationMap(eventDeactivationMap), + mIsActive(mEventActivationMap.empty()), + mSlicedStateAtoms(slicedStateAtoms), + mStateGroupMap(stateGroupMap) { +} void MetricProducer::onMatchedLogEventLocked(const size_t matcherIndex, const LogEvent& event) { if (!mIsActive) { diff --git a/cmds/statsd/src/metrics/MetricProducer.h b/cmds/statsd/src/metrics/MetricProducer.h index 1e1eb69aa8f0..a72de22bd433 100644 --- a/cmds/statsd/src/metrics/MetricProducer.h +++ b/cmds/statsd/src/metrics/MetricProducer.h @@ -17,19 +17,19 @@ #ifndef METRIC_PRODUCER_H #define METRIC_PRODUCER_H -#include <shared_mutex> - #include <frameworks/base/cmds/statsd/src/active_config_list.pb.h> +#include <log/logprint.h> +#include <utils/RefBase.h> + +#include <unordered_map> + #include "HashableDimensionKey.h" #include "anomaly/AnomalyTracker.h" #include "condition/ConditionWizard.h" #include "config/ConfigKey.h" #include "matchers/matcher_util.h" #include "packages/PackageInfoListener.h" - -#include <log/logprint.h> -#include <utils/RefBase.h> -#include <unordered_map> +#include "state/StateListener.h" namespace android { namespace os { @@ -86,13 +86,15 @@ struct Activation { // writing the report to dropbox. MetricProducers should respond to package changes as required in // PackageInfoListener, but if none of the metrics are slicing by package name, then the update can // be a no-op. -class MetricProducer : public virtual PackageInfoListener { +class MetricProducer : public virtual PackageInfoListener, public virtual StateListener { public: MetricProducer(const int64_t& metricId, const ConfigKey& key, const int64_t timeBaseNs, const int conditionIndex, const sp<ConditionWizard>& wizard, const std::unordered_map<int, std::shared_ptr<Activation>>& eventActivationMap, const std::unordered_map<int, std::vector<std::shared_ptr<Activation>>>& - eventDeactivationMap); + eventDeactivationMap, + const vector<int>& slicedStateAtoms, + const unordered_map<int, unordered_map<int, int64_t>>& stateGroupMap); virtual ~MetricProducer(){}; @@ -151,6 +153,9 @@ public: return mConditionSliced; }; + void onStateChanged(int atomId, const HashableDimensionKey& primaryKey, int oldState, + int newState){}; + // Output the metrics data to [protoOutput]. All metrics reports end with the same timestamp. // This method clears all the past buckets. void onDumpReport(const int64_t dumpTimeNs, @@ -230,6 +235,11 @@ public: return mBucketSizeNs; } + inline const std::vector<int> getSlicedStateAtoms() { + std::lock_guard<std::mutex> lock(mMutex); + return mSlicedStateAtoms; + } + /* If alert is valid, adds an AnomalyTracker and returns it. If invalid, returns nullptr. */ virtual sp<AnomalyTracker> addAnomalyTracker(const Alert &alert, const sp<AlarmMonitor>& anomalyAlarmMonitor) { @@ -381,6 +391,16 @@ protected: bool mIsActive; + // The slice_by_state atom ids defined in statsd_config. + std::vector<int> mSlicedStateAtoms; + + // Maps atom ids and state values to group_ids (<atom_id, <value, group_id>>). + std::unordered_map<int, std::unordered_map<int, int64_t>> mStateGroupMap; + + FRIEND_TEST(CountMetricE2eTest, TestWithSimpleState); + FRIEND_TEST(CountMetricE2eTest, TestWithMappedState); + FRIEND_TEST(CountMetricE2eTest, TestWithMultipleStates); + FRIEND_TEST(DurationMetricE2eTest, TestOneBucket); FRIEND_TEST(DurationMetricE2eTest, TestTwoBuckets); FRIEND_TEST(DurationMetricE2eTest, TestWithActivation); diff --git a/cmds/statsd/src/metrics/MetricsManager.cpp b/cmds/statsd/src/metrics/MetricsManager.cpp index 963205ee56f4..7bae4b929b48 100644 --- a/cmds/statsd/src/metrics/MetricsManager.cpp +++ b/cmds/statsd/src/metrics/MetricsManager.cpp @@ -15,8 +15,12 @@ */ #define DEBUG false // STOPSHIP if true #include "Log.h" + #include "MetricsManager.h" -#include "statslog.h" + +#include <log/logprint.h> +#include <private/android_filesystem_config.h> +#include <utils/SystemClock.h> #include "CountMetricProducer.h" #include "condition/CombinationConditionTracker.h" @@ -25,12 +29,10 @@ #include "matchers/CombinationLogMatchingTracker.h" #include "matchers/SimpleLogMatchingTracker.h" #include "metrics_manager_util.h" -#include "stats_util.h" +#include "state/StateManager.h" #include "stats_log_util.h" - -#include <log/logprint.h> -#include <private/android_filesystem_config.h> -#include <utils/SystemClock.h> +#include "stats_util.h" +#include "statslog.h" using android::util::FIELD_COUNT_REPEATED; using android::util::FIELD_TYPE_INT32; @@ -149,6 +151,12 @@ MetricsManager::MetricsManager(const ConfigKey& key, const StatsdConfig& config, } MetricsManager::~MetricsManager() { + for (auto it : mAllMetricProducers) { + for (int atomId : it->getSlicedStateAtoms()) { + StateManager::getInstance().unregisterListener(atomId, it); + } + } + VLOG("~MetricsManager()"); } diff --git a/cmds/statsd/src/metrics/MetricsManager.h b/cmds/statsd/src/metrics/MetricsManager.h index 3dad61465416..d184121f5ba0 100644 --- a/cmds/statsd/src/metrics/MetricsManager.h +++ b/cmds/statsd/src/metrics/MetricsManager.h @@ -282,6 +282,10 @@ private: TestActivationOnBootMultipleActivationsDifferentActivationTypes); FRIEND_TEST(StatsLogProcessorTest, TestActivationsPersistAcrossSystemServerRestart); + FRIEND_TEST(CountMetricE2eTest, TestWithSimpleState); + FRIEND_TEST(CountMetricE2eTest, TestWithMappedState); + FRIEND_TEST(CountMetricE2eTest, TestWithMultipleStates); + FRIEND_TEST(DurationMetricE2eTest, TestOneBucket); FRIEND_TEST(DurationMetricE2eTest, TestTwoBuckets); FRIEND_TEST(DurationMetricE2eTest, TestWithActivation); diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.cpp b/cmds/statsd/src/metrics/ValueMetricProducer.cpp index 7fe5a834f412..6fd03273a434 100644 --- a/cmds/statsd/src/metrics/ValueMetricProducer.cpp +++ b/cmds/statsd/src/metrics/ValueMetricProducer.cpp @@ -83,9 +83,11 @@ ValueMetricProducer::ValueMetricProducer( const sp<EventMatcherWizard>& matcherWizard, const int pullTagId, const int64_t timeBaseNs, const int64_t startTimeNs, const sp<StatsPullerManager>& pullerManager, const unordered_map<int, shared_ptr<Activation>>& eventActivationMap, - const unordered_map<int, vector<shared_ptr<Activation>>>& eventDeactivationMap) + const unordered_map<int, vector<shared_ptr<Activation>>>& eventDeactivationMap, + const vector<int>& slicedStateAtoms, + const unordered_map<int, unordered_map<int, int64_t>>& stateGroupMap) : MetricProducer(metric.id(), key, timeBaseNs, conditionIndex, conditionWizard, - eventActivationMap, eventDeactivationMap), + eventActivationMap, eventDeactivationMap, slicedStateAtoms, stateGroupMap), mWhatMatcherIndex(whatMatcherIndex), mEventMatcherWizard(matcherWizard), mPullerManager(pullerManager), diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.h b/cmds/statsd/src/metrics/ValueMetricProducer.h index d7cd397da2c0..206e602dd1c7 100644 --- a/cmds/statsd/src/metrics/ValueMetricProducer.h +++ b/cmds/statsd/src/metrics/ValueMetricProducer.h @@ -52,15 +52,17 @@ struct ValueBucket { // - an alarm set to the end of the bucket class ValueMetricProducer : public virtual MetricProducer, public virtual PullDataReceiver { public: - ValueMetricProducer(const ConfigKey& key, const ValueMetric& valueMetric, - const int conditionIndex, const sp<ConditionWizard>& conditionWizard, - const int whatMatcherIndex, const sp<EventMatcherWizard>& matcherWizard, - const int pullTagId, const int64_t timeBaseNs, const int64_t startTimeNs, - const sp<StatsPullerManager>& pullerManager, - const std::unordered_map<int, std::shared_ptr<Activation>>& - eventActivationMap = {}, - const std::unordered_map<int, std::vector<std::shared_ptr<Activation>>>& - eventDeactivationMap = {}); + ValueMetricProducer( + const ConfigKey& key, const ValueMetric& valueMetric, const int conditionIndex, + const sp<ConditionWizard>& conditionWizard, const int whatMatcherIndex, + const sp<EventMatcherWizard>& matcherWizard, const int pullTagId, + const int64_t timeBaseNs, const int64_t startTimeNs, + const sp<StatsPullerManager>& pullerManager, + const std::unordered_map<int, std::shared_ptr<Activation>>& eventActivationMap = {}, + const std::unordered_map<int, std::vector<std::shared_ptr<Activation>>>& + eventDeactivationMap = {}, + const vector<int>& slicedStateAtoms = {}, + const unordered_map<int, unordered_map<int, int64_t>>& stateGroupMap = {}); virtual ~ValueMetricProducer(); diff --git a/cmds/statsd/src/metrics/metrics_manager_util.cpp b/cmds/statsd/src/metrics/metrics_manager_util.cpp index 0fee71e1fe2c..33e162ec2d24 100644 --- a/cmds/statsd/src/metrics/metrics_manager_util.cpp +++ b/cmds/statsd/src/metrics/metrics_manager_util.cpp @@ -20,24 +20,24 @@ #include "metrics_manager_util.h" #include "MetricProducer.h" -#include "../condition/CombinationConditionTracker.h" -#include "../condition/SimpleConditionTracker.h" -#include "../condition/StateConditionTracker.h" -#include "../external/StatsPullerManager.h" -#include "../matchers/CombinationLogMatchingTracker.h" -#include "../matchers/SimpleLogMatchingTracker.h" -#include "../matchers/EventMatcherWizard.h" -#include "../metrics/CountMetricProducer.h" -#include "../metrics/DurationMetricProducer.h" -#include "../metrics/EventMetricProducer.h" -#include "../metrics/GaugeMetricProducer.h" -#include "../metrics/ValueMetricProducer.h" +#include <inttypes.h> +#include "condition/CombinationConditionTracker.h" +#include "condition/SimpleConditionTracker.h" +#include "condition/StateConditionTracker.h" +#include "external/StatsPullerManager.h" +#include "matchers/CombinationLogMatchingTracker.h" +#include "matchers/EventMatcherWizard.h" +#include "matchers/SimpleLogMatchingTracker.h" +#include "metrics/CountMetricProducer.h" +#include "metrics/DurationMetricProducer.h" +#include "metrics/EventMetricProducer.h" +#include "metrics/GaugeMetricProducer.h" +#include "metrics/ValueMetricProducer.h" +#include "state/StateManager.h" #include "stats_util.h" #include "statslog.h" -#include <inttypes.h> - using std::set; using std::string; using std::unordered_map; @@ -138,6 +138,41 @@ bool handleMetricWithConditions( return true; } +// Initializes state data structures for a metric. +// input: +// [config]: the input config +// [stateIds]: the slice_by_state ids for this metric +// [stateAtomIdMap]: this map contains the mapping from all state ids to atom ids +// [allStateGroupMaps]: this map contains the mapping from state ids and state +// values to state group ids for all states +// output: +// [slicedStateAtoms]: a vector of atom ids of all the slice_by_states +// [stateGroupMap]: this map should contain the mapping from states ids and state +// values to state group ids for all states that this metric +// is interested in +bool handleMetricWithStates( + const StatsdConfig& config, const ::google::protobuf::RepeatedField<int64_t>& stateIds, + const unordered_map<int64_t, int>& stateAtomIdMap, + const unordered_map<int64_t, unordered_map<int, int64_t>>& allStateGroupMaps, + vector<int>& slicedStateAtoms, + unordered_map<int, unordered_map<int, int64_t>>& stateGroupMap) { + for (const auto& stateId : stateIds) { + auto it = stateAtomIdMap.find(stateId); + if (it == stateAtomIdMap.end()) { + ALOGW("cannot find State %" PRId64 " in the config", stateId); + return false; + } + int atomId = it->second; + slicedStateAtoms.push_back(atomId); + + auto stateIt = allStateGroupMaps.find(stateId); + if (stateIt != allStateGroupMaps.end()) { + stateGroupMap[atomId] = stateIt->second; + } + } + return true; +} + // Validates a metricActivation and populates state. // EventActivationMap and EventDeactivationMap are supplied to a MetricProducer // to provide the producer with state about its activators and deactivators. @@ -342,12 +377,32 @@ bool initConditions(const ConfigKey& key, const StatsdConfig& config, return true; } +bool initStates(const StatsdConfig& config, unordered_map<int64_t, int>& stateAtomIdMap, + unordered_map<int64_t, unordered_map<int, int64_t>>& allStateGroupMaps) { + for (int i = 0; i < config.state_size(); i++) { + const State& state = config.state(i); + const int64_t stateId = state.id(); + stateAtomIdMap[stateId] = state.atom_id(); + + const StateMap& stateMap = state.map(); + for (auto group : stateMap.group()) { + for (auto value : group.value()) { + allStateGroupMaps[stateId][value] = group.group_id(); + } + } + } + + return true; +} + bool initMetrics(const ConfigKey& key, const StatsdConfig& config, const int64_t timeBaseTimeNs, const int64_t currentTimeNs, UidMap& uidMap, const sp<StatsPullerManager>& pullerManager, const unordered_map<int64_t, int>& logTrackerMap, const unordered_map<int64_t, int>& conditionTrackerMap, const vector<sp<LogMatchingTracker>>& allAtomMatchers, + const unordered_map<int64_t, int>& stateAtomIdMap, + const unordered_map<int64_t, unordered_map<int, int64_t>>& allStateGroupMaps, vector<sp<ConditionTracker>>& allConditionTrackers, vector<sp<MetricProducer>>& allMetricProducers, unordered_map<int, vector<int>>& conditionToMetricMap, @@ -398,10 +453,9 @@ bool initMetrics(const ConfigKey& key, const StatsdConfig& config, const int64_t int conditionIndex = -1; if (metric.has_condition()) { - bool good = handleMetricWithConditions( - metric.condition(), metricIndex, conditionTrackerMap, metric.links(), - allConditionTrackers, conditionIndex, conditionToMetricMap); - if (!good) { + if (!handleMetricWithConditions(metric.condition(), metricIndex, conditionTrackerMap, + metric.links(), allConditionTrackers, conditionIndex, + conditionToMetricMap)) { return false; } } else { @@ -411,6 +465,18 @@ bool initMetrics(const ConfigKey& key, const StatsdConfig& config, const int64_t } } + std::vector<int> slicedStateAtoms; + unordered_map<int, unordered_map<int, int64_t>> stateGroupMap; + if (metric.slice_by_state_size() > 0) { + if (!handleMetricWithStates(config, metric.slice_by_state(), stateAtomIdMap, + allStateGroupMaps, slicedStateAtoms, stateGroupMap)) { + return false; + } + } + + // TODO(tsaichristine): add check for unequal number of MetricStateLinks + // and slice_by_states + unordered_map<int, shared_ptr<Activation>> eventActivationMap; unordered_map<int, vector<shared_ptr<Activation>>> eventDeactivationMap; bool success = handleMetricActivation(config, metric.id(), metricIndex, @@ -421,7 +487,7 @@ bool initMetrics(const ConfigKey& key, const StatsdConfig& config, const int64_t sp<MetricProducer> countProducer = new CountMetricProducer( key, metric, conditionIndex, wizard, timeBaseTimeNs, currentTimeNs, - eventActivationMap, eventDeactivationMap); + eventActivationMap, eventDeactivationMap, slicedStateAtoms, stateGroupMap); allMetricProducers.push_back(countProducer); } @@ -721,6 +787,13 @@ bool initMetrics(const ConfigKey& key, const StatsdConfig& config, const int64_t } for (const auto& it : allMetricProducers) { uidMap.addListener(it); + + // Register metrics to StateTrackers + for (int atomId : it->getSlicedStateAtoms()) { + if (!StateManager::getInstance().registerListener(atomId, it)) { + return false; + } + } } return true; } @@ -846,6 +919,8 @@ bool initStatsdConfig(const ConfigKey& key, const StatsdConfig& config, UidMap& unordered_map<int64_t, int> logTrackerMap; unordered_map<int64_t, int> conditionTrackerMap; unordered_map<int64_t, int> metricProducerMap; + unordered_map<int64_t, int> stateAtomIdMap; + unordered_map<int64_t, unordered_map<int, int64_t>> allStateGroupMaps; if (!initLogTrackers(config, uidMap, logTrackerMap, allAtomMatchers, allTagIds)) { ALOGE("initLogMatchingTrackers failed"); @@ -858,9 +933,13 @@ bool initStatsdConfig(const ConfigKey& key, const StatsdConfig& config, UidMap& ALOGE("initConditionTrackers failed"); return false; } - + if (!initStates(config, stateAtomIdMap, allStateGroupMaps)) { + ALOGE("initStates failed"); + return false; + } if (!initMetrics(key, config, timeBaseNs, currentTimeNs, uidMap, pullerManager, logTrackerMap, - conditionTrackerMap, allAtomMatchers, allConditionTrackers, allMetricProducers, + conditionTrackerMap, allAtomMatchers, stateAtomIdMap, allStateGroupMaps, + allConditionTrackers, allMetricProducers, conditionToMetricMap, trackerToMetricMap, metricProducerMap, noReportMetricIds, activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap, metricsWithActivation)) { diff --git a/cmds/statsd/src/metrics/metrics_manager_util.h b/cmds/statsd/src/metrics/metrics_manager_util.h index 3802948f64da..95b2ab81fa53 100644 --- a/cmds/statsd/src/metrics/metrics_manager_util.h +++ b/cmds/statsd/src/metrics/metrics_manager_util.h @@ -68,6 +68,17 @@ bool initConditions(const ConfigKey& key, const StatsdConfig& config, std::unordered_map<int, std::vector<int>>& trackerToConditionMap, std::unordered_map<int, std::vector<MetricConditionLink>>& eventConditionLinks); +// Initialize State maps using State protos in the config. These maps will +// eventually be passed to MetricProducers to initialize their state info. +// input: +// [config]: the input config +// output: +// [stateAtomIdMap]: this map should contain the mapping from state ids to atom ids +// [allStateGroupMaps]: this map should contain the mapping from states ids and state +// values to state group ids for all states +bool initStates(const StatsdConfig& config, unordered_map<int64_t, int>& stateAtomIdMap, + unordered_map<int64_t, unordered_map<int, int64_t>>& allStateGroupMaps); + // Initialize MetricProducers. // input: // [key]: the config key that this config belongs to @@ -75,6 +86,9 @@ bool initConditions(const ConfigKey& key, const StatsdConfig& config, // [timeBaseSec]: start time base for all metrics // [logTrackerMap]: LogMatchingTracker name to index mapping from previous step. // [conditionTrackerMap]: condition name to index mapping +// [stateAtomIdMap]: contains the mapping from state ids to atom ids +// [allStateGroupMaps]: contains the mapping from atom ids and state values to +// state group ids for all states // output: // [allMetricProducers]: contains the list of sp to the MetricProducers created. // [conditionToMetricMap]: contains the mapping from condition tracker index to @@ -87,6 +101,8 @@ bool initMetrics( const std::unordered_map<int64_t, int>& conditionTrackerMap, const std::unordered_map<int, std::vector<MetricConditionLink>>& eventConditionLinks, const vector<sp<LogMatchingTracker>>& allAtomMatchers, + const unordered_map<int64_t, int>& stateAtomIdMap, + const unordered_map<int64_t, unordered_map<int, int64_t>>& allStateGroupMaps, vector<sp<ConditionTracker>>& allConditionTrackers, std::vector<sp<MetricProducer>>& allMetricProducers, std::unordered_map<int, std::vector<int>>& conditionToMetricMap, diff --git a/cmds/statsd/src/state/StateManager.cpp b/cmds/statsd/src/state/StateManager.cpp index a3059c5b52ac..95b2c7691803 100644 --- a/cmds/statsd/src/state/StateManager.cpp +++ b/cmds/statsd/src/state/StateManager.cpp @@ -35,26 +35,25 @@ void StateManager::onLogEvent(const LogEvent& event) { } } -bool StateManager::registerListener(int stateAtomId, wp<StateListener> listener) { +bool StateManager::registerListener(int atomId, wp<StateListener> listener) { std::lock_guard<std::mutex> lock(mMutex); // Check if state tracker already exists - if (mStateTrackers.find(stateAtomId) == mStateTrackers.end()) { + if (mStateTrackers.find(atomId) == mStateTrackers.end()) { // Create a new state tracker iff atom is a state atom - auto it = android::util::AtomsInfo::kStateAtomsFieldOptions.find(stateAtomId); + auto it = android::util::AtomsInfo::kStateAtomsFieldOptions.find(atomId); if (it != android::util::AtomsInfo::kStateAtomsFieldOptions.end()) { - mStateTrackers[stateAtomId] = new StateTracker(stateAtomId, it->second); + mStateTrackers[atomId] = new StateTracker(atomId, it->second); } else { - ALOGE("StateManager cannot register listener, Atom %d is not a state atom", - stateAtomId); + ALOGE("StateManager cannot register listener, Atom %d is not a state atom", atomId); return false; } } - mStateTrackers[stateAtomId]->registerListener(listener); + mStateTrackers[atomId]->registerListener(listener); return true; } -void StateManager::unregisterListener(int stateAtomId, wp<StateListener> listener) { +void StateManager::unregisterListener(int atomId, wp<StateListener> listener) { std::unique_lock<std::mutex> lock(mMutex); // Hold the sp<> until the lock is released so that ~StateTracker() is @@ -62,7 +61,7 @@ void StateManager::unregisterListener(int stateAtomId, wp<StateListener> listene sp<StateTracker> toRemove; // Unregister listener from correct StateTracker - auto it = mStateTrackers.find(stateAtomId); + auto it = mStateTrackers.find(atomId); if (it != mStateTrackers.end()) { it->second->unregisterListener(listener); @@ -73,15 +72,15 @@ void StateManager::unregisterListener(int stateAtomId, wp<StateListener> listene } } else { ALOGE("StateManager cannot unregister listener, StateTracker for atom %d does not exist", - stateAtomId); + atomId); } lock.unlock(); } -int StateManager::getState(int stateAtomId, const HashableDimensionKey& key) { +int StateManager::getStateValue(int atomId, const HashableDimensionKey& key) { std::lock_guard<std::mutex> lock(mMutex); - if (mStateTrackers.find(stateAtomId) != mStateTrackers.end()) { - return mStateTrackers[stateAtomId]->getState(key); + if (mStateTrackers.find(atomId) != mStateTrackers.end()) { + return mStateTrackers[atomId]->getStateValue(key); } return StateTracker::kStateUnknown; diff --git a/cmds/statsd/src/state/StateManager.h b/cmds/statsd/src/state/StateManager.h index ce60f1482be7..89ee6c04b922 100644 --- a/cmds/statsd/src/state/StateManager.h +++ b/cmds/statsd/src/state/StateManager.h @@ -15,10 +15,11 @@ */ #pragma once -//#include <utils/Log.h> +#include <gtest/gtest_prod.h> +#include <inttypes.h> #include <utils/RefBase.h> -#include "HashableDimensionKey.h" +#include "HashableDimensionKey.h" #include "state/StateListener.h" #include "state/StateTracker.h" @@ -38,29 +39,29 @@ public: // Notifies the correct StateTracker of an event. void onLogEvent(const LogEvent& event); - // Returns true if stateAtomId is the id of a state atom and notifies the - // correct StateTracker to register the listener. If the correct - // StateTracker does not exist, a new StateTracker is created. - bool registerListener(int stateAtomId, wp<StateListener> listener); + // Returns true if atomId is being tracked and is associated with a state + // atom. StateManager notifies the correct StateTracker to register listener. + // If the correct StateTracker does not exist, a new StateTracker is created. + bool registerListener(int atomId, wp<StateListener> listener); // Notifies the correct StateTracker to unregister a listener // and removes the tracker if it no longer has any listeners. - void unregisterListener(int stateAtomId, wp<StateListener> listener); + void unregisterListener(int atomId, wp<StateListener> listener); - // Queries the correct StateTracker for the state that is mapped to the given - // query key. + // Queries the correct StateTracker for the original/un-mapped state value + // that is mapped to the given query key. // If the StateTracker doesn't exist, returns StateTracker::kStateUnknown. - int getState(int stateAtomId, const HashableDimensionKey& queryKey); + int getStateValue(int atomId, const HashableDimensionKey& queryKey); inline int getStateTrackersCount() { std::lock_guard<std::mutex> lock(mMutex); return mStateTrackers.size(); } - inline int getListenersCount(int stateAtomId) { + inline int getListenersCount(int atomId) { std::lock_guard<std::mutex> lock(mMutex); - if (mStateTrackers.find(stateAtomId) != mStateTrackers.end()) { - return mStateTrackers[stateAtomId]->getListenersCount(); + if (mStateTrackers.find(atomId) != mStateTrackers.end()) { + return mStateTrackers[atomId]->getListenersCount(); } return -1; } diff --git a/cmds/statsd/src/state/StateTracker.cpp b/cmds/statsd/src/state/StateTracker.cpp index 5a91950b9f8b..323fc0e94ab2 100644 --- a/cmds/statsd/src/state/StateTracker.cpp +++ b/cmds/statsd/src/state/StateTracker.cpp @@ -30,13 +30,13 @@ StateTracker::StateTracker(const int atomId, : mAtomId(atomId), mStateField(getSimpleMatcher(atomId, stateAtomInfo.exclusiveField)) { // create matcher for each primary field - // TODO(tsaichristine): handle when primary field is first uid in chain + // TODO(tsaichristine): b/142108433 handle when primary field is first uid in chain for (const auto& primary : stateAtomInfo.primaryFields) { Matcher matcher = getSimpleMatcher(atomId, primary); mPrimaryFields.push_back(matcher); } - // TODO(tsaichristine): set default state, reset state, and nesting + // TODO(tsaichristine): b/142108433 set default state, reset state, and nesting } void StateTracker::onLogEvent(const LogEvent& event) { @@ -96,7 +96,7 @@ void StateTracker::unregisterListener(wp<StateListener> listener) { mListeners.erase(listener); } -int StateTracker::getState(const HashableDimensionKey& queryKey) const { +int StateTracker::getStateValue(const HashableDimensionKey& queryKey) const { if (queryKey.getValues().size() == mPrimaryFields.size()) { auto it = mStateMap.find(queryKey); if (it != mStateMap.end()) { diff --git a/cmds/statsd/src/state/StateTracker.h b/cmds/statsd/src/state/StateTracker.h index f22706c8418d..cfa9fd8f6272 100644 --- a/cmds/statsd/src/state/StateTracker.h +++ b/cmds/statsd/src/state/StateTracker.h @@ -48,7 +48,7 @@ public: // Returns the state value mapped to the given query key. // If the key isn't mapped to a state or the key size doesn't match the // primary key size, the default state is returned. - int getState(const HashableDimensionKey& queryKey) const; + int getStateValue(const HashableDimensionKey& queryKey) const; inline int getListenersCount() const { return mListeners.size(); diff --git a/cmds/statsd/src/stats_log_util.cpp b/cmds/statsd/src/stats_log_util.cpp index 67625eb82454..c22e3cc07a3b 100644 --- a/cmds/statsd/src/stats_log_util.cpp +++ b/cmds/statsd/src/stats_log_util.cpp @@ -445,6 +445,8 @@ int64_t TimeUnitToBucketSizeInMillis(TimeUnit unit) { return 12 * 60 * 60 * 1000LL; case ONE_DAY: return 24 * 60 * 60 * 1000LL; + case ONE_WEEK: + return 7 * 24 * 60 * 60 * 1000LL; case CTS: return 1000; case TIME_UNIT_UNSPECIFIED: diff --git a/cmds/statsd/src/statsd_config.proto b/cmds/statsd/src/statsd_config.proto index c107397b0273..c98b2cf4b472 100644 --- a/cmds/statsd/src/statsd_config.proto +++ b/cmds/statsd/src/statsd_config.proto @@ -44,6 +44,7 @@ enum TimeUnit { SIX_HOURS = 7; TWELVE_HOURS = 8; ONE_DAY = 9; + ONE_WEEK = 10; CTS = 1000; } diff --git a/cmds/statsd/tests/e2e/CountMetric_e2e_test.cpp b/cmds/statsd/tests/e2e/CountMetric_e2e_test.cpp new file mode 100644 index 000000000000..6591d69cdc1a --- /dev/null +++ b/cmds/statsd/tests/e2e/CountMetric_e2e_test.cpp @@ -0,0 +1,197 @@ +/* + * Copyright (C) 2019, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <gtest/gtest.h> + +#include "src/StatsLogProcessor.h" +#include "src/state/StateManager.h" +#include "tests/statsd_test_util.h" + +namespace android { +namespace os { +namespace statsd { + +#ifdef __ANDROID__ + +TEST(CountMetricE2eTest, TestWithSimpleState) { + // Initialize config + StatsdConfig config; + config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root. + + auto syncStartMatcher = CreateSyncStartAtomMatcher(); + *config.add_atom_matcher() = syncStartMatcher; + + auto state = CreateScreenState(); + *config.add_state() = state; + + // Create count metric that slices by screen state + int64_t metricId = 123456; + auto countMetric = config.add_count_metric(); + countMetric->set_id(metricId); + countMetric->set_what(syncStartMatcher.id()); + countMetric->set_bucket(TimeUnit::ONE_MINUTE); + countMetric->add_slice_by_state(state.id()); + + // Initialize StatsLogProcessor + const int64_t baseTimeNs = 0; // 0:00 + const int64_t configAddedTimeNs = baseTimeNs + 1 * NS_PER_SEC; // 0:01 + const int64_t bucketSizeNs = + TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000LL * 1000LL; + + int uid = 12345; + int64_t cfgId = 98765; + ConfigKey cfgKey(uid, cfgId); + + auto processor = CreateStatsLogProcessor(baseTimeNs, configAddedTimeNs, config, cfgKey); + + // Check that StateTrackers were properly initialized. + EXPECT_EQ(1, StateManager::getInstance().getStateTrackersCount()); + EXPECT_EQ(1, + StateManager::getInstance().getListenersCount(android::util::SCREEN_STATE_CHANGED)); + + // Check that CountMetricProducer was initialized correctly. + EXPECT_EQ(processor->mMetricsManagers.size(), 1u); + sp<MetricsManager> metricsManager = processor->mMetricsManagers.begin()->second; + EXPECT_TRUE(metricsManager->isConfigValid()); + EXPECT_EQ(metricsManager->mAllMetricProducers.size(), 1); + sp<MetricProducer> metricProducer = metricsManager->mAllMetricProducers[0]; + EXPECT_EQ(metricProducer->mSlicedStateAtoms.size(), 1); + EXPECT_EQ(metricProducer->mSlicedStateAtoms.at(0), android::util::SCREEN_STATE_CHANGED); + EXPECT_EQ(metricProducer->mStateGroupMap.size(), 0); +} + +TEST(CountMetricE2eTest, TestWithMappedState) { + // Initialize config + StatsdConfig config; + config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root. + + auto syncStartMatcher = CreateSyncStartAtomMatcher(); + *config.add_atom_matcher() = syncStartMatcher; + + auto state = CreateScreenStateWithOnOffMap(); + *config.add_state() = state; + + // Create count metric that slices by screen state with on/off map + int64_t metricId = 123456; + auto countMetric = config.add_count_metric(); + countMetric->set_id(metricId); + countMetric->set_what(syncStartMatcher.id()); + countMetric->set_bucket(TimeUnit::ONE_MINUTE); + countMetric->add_slice_by_state(state.id()); + + // Initialize StatsLogProcessor + const int64_t baseTimeNs = 0; // 0:00 + const int64_t configAddedTimeNs = baseTimeNs + 1 * NS_PER_SEC; // 0:01 + const int64_t bucketSizeNs = + TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000LL * 1000LL; + + int uid = 12345; + int64_t cfgId = 98765; + ConfigKey cfgKey(uid, cfgId); + + auto processor = CreateStatsLogProcessor(baseTimeNs, configAddedTimeNs, config, cfgKey); + + // Check that StateTrackers were properly initialized. + EXPECT_EQ(1, StateManager::getInstance().getStateTrackersCount()); + EXPECT_EQ(1, + StateManager::getInstance().getListenersCount(android::util::SCREEN_STATE_CHANGED)); + + // Check that CountMetricProducer was initialized correctly. + EXPECT_EQ(processor->mMetricsManagers.size(), 1u); + sp<MetricsManager> metricsManager = processor->mMetricsManagers.begin()->second; + EXPECT_TRUE(metricsManager->isConfigValid()); + EXPECT_EQ(metricsManager->mAllMetricProducers.size(), 1); + sp<MetricProducer> metricProducer = metricsManager->mAllMetricProducers[0]; + EXPECT_EQ(metricProducer->mSlicedStateAtoms.size(), 1); + EXPECT_EQ(metricProducer->mSlicedStateAtoms.at(0), android::util::SCREEN_STATE_CHANGED); + EXPECT_EQ(metricProducer->mStateGroupMap.size(), 1); + + StateMap map = state.map(); + for (auto group : map.group()) { + for (auto value : group.value()) { + EXPECT_EQ(metricProducer->mStateGroupMap[android::util::SCREEN_STATE_CHANGED][value], + group.group_id()); + } + } +} + +TEST(CountMetricE2eTest, TestWithMultipleStates) { + // Initialize config + StatsdConfig config; + config.add_allowed_log_source("AID_ROOT"); // LogEvent defaults to UID of root. + + auto syncStartMatcher = CreateSyncStartAtomMatcher(); + *config.add_atom_matcher() = syncStartMatcher; + + auto state1 = CreateScreenStateWithOnOffMap(); + *config.add_state() = state1; + auto state2 = CreateUidProcessState(); + *config.add_state() = state2; + + // Create count metric that slices by screen state with on/off map + int64_t metricId = 123456; + auto countMetric = config.add_count_metric(); + countMetric->set_id(metricId); + countMetric->set_what(syncStartMatcher.id()); + countMetric->set_bucket(TimeUnit::ONE_MINUTE); + countMetric->add_slice_by_state(state1.id()); + countMetric->add_slice_by_state(state2.id()); + + // Initialize StatsLogProcessor + const int64_t baseTimeNs = 0; // 0:00 + const int64_t configAddedTimeNs = baseTimeNs + 1 * NS_PER_SEC; // 0:01 + const int64_t bucketSizeNs = + TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000LL * 1000LL; + + int uid = 12345; + int64_t cfgId = 98765; + ConfigKey cfgKey(uid, cfgId); + + auto processor = CreateStatsLogProcessor(baseTimeNs, configAddedTimeNs, config, cfgKey); + + // Check that StateTrackers were properly initialized. + EXPECT_EQ(2, StateManager::getInstance().getStateTrackersCount()); + EXPECT_EQ(1, + StateManager::getInstance().getListenersCount(android::util::SCREEN_STATE_CHANGED)); + EXPECT_EQ(1, StateManager::getInstance().getListenersCount( + android::util::UID_PROCESS_STATE_CHANGED)); + + // Check that CountMetricProducer was initialized correctly. + EXPECT_EQ(processor->mMetricsManagers.size(), 1u); + sp<MetricsManager> metricsManager = processor->mMetricsManagers.begin()->second; + EXPECT_TRUE(metricsManager->isConfigValid()); + EXPECT_EQ(metricsManager->mAllMetricProducers.size(), 1); + sp<MetricProducer> metricProducer = metricsManager->mAllMetricProducers[0]; + EXPECT_EQ(metricProducer->mSlicedStateAtoms.size(), 2); + EXPECT_EQ(metricProducer->mSlicedStateAtoms.at(0), android::util::SCREEN_STATE_CHANGED); + EXPECT_EQ(metricProducer->mSlicedStateAtoms.at(1), android::util::UID_PROCESS_STATE_CHANGED); + EXPECT_EQ(metricProducer->mStateGroupMap.size(), 1); + + StateMap map = state1.map(); + for (auto group : map.group()) { + for (auto value : group.value()) { + EXPECT_EQ(metricProducer->mStateGroupMap[android::util::SCREEN_STATE_CHANGED][value], + group.group_id()); + } + } +} + +} // namespace statsd +} // namespace os +} // namespace android +#else +GTEST_LOG_(INFO) << "This test does nothing.\n"; +#endif diff --git a/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp b/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp index 839daa4b7f8a..8915c7364bc7 100644 --- a/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp +++ b/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp @@ -43,8 +43,8 @@ TEST(CountMetricProducerTest, TestFirstBucket) { metric.set_bucket(ONE_MINUTE); sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); - CountMetricProducer countProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard, - 5, 600 * NS_PER_SEC + NS_PER_SEC/2); + CountMetricProducer countProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard, 5, + 600 * NS_PER_SEC + NS_PER_SEC / 2); EXPECT_EQ(600500000000, countProducer.mCurrentBucketStartTimeNs); EXPECT_EQ(10, countProducer.mCurrentBucketNum); EXPECT_EQ(660000000005, countProducer.getCurrentBucketEndTimeNs()); @@ -131,7 +131,8 @@ TEST(CountMetricProducerTest, TestEventsWithNonSlicedCondition) { sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); - CountMetricProducer countProducer(kConfigKey, metric, 1, wizard, bucketStartTimeNs, bucketStartTimeNs); + CountMetricProducer countProducer(kConfigKey, metric, 1, wizard, bucketStartTimeNs, + bucketStartTimeNs); countProducer.onConditionChanged(true, bucketStartTimeNs); countProducer.onMatchedLogEvent(1 /*matcher index*/, event1); @@ -396,6 +397,26 @@ TEST(CountMetricProducerTest, TestAnomalyDetectionUnSliced) { std::ceil(1.0 * event7.GetElapsedTimestampNs() / NS_PER_SEC + refPeriodSec)); } +TEST(CountMetricProducerTest, TestOneWeekTimeUnit) { + CountMetric metric; + metric.set_id(1); + metric.set_bucket(ONE_WEEK); + + sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>(); + + int64_t oneDayNs = 24 * 60 * 60 * 1e9; + int64_t fiveWeeksNs = 5 * 7 * oneDayNs; + + CountMetricProducer countProducer( + kConfigKey, metric, -1 /* meaning no condition */, wizard, oneDayNs, fiveWeeksNs); + + int64_t fiveWeeksOneDayNs = fiveWeeksNs + oneDayNs; + + EXPECT_EQ(fiveWeeksNs, countProducer.mCurrentBucketStartTimeNs); + EXPECT_EQ(4, countProducer.mCurrentBucketNum); + EXPECT_EQ(fiveWeeksOneDayNs, countProducer.getCurrentBucketEndTimeNs()); +} + } // namespace statsd } // namespace os } // namespace android diff --git a/cmds/statsd/tests/state/StateTracker_test.cpp b/cmds/statsd/tests/state/StateTracker_test.cpp index c89ffea85709..8d380006a8ab 100644 --- a/cmds/statsd/tests/state/StateTracker_test.cpp +++ b/cmds/statsd/tests/state/StateTracker_test.cpp @@ -44,7 +44,7 @@ public: std::vector<Update> updates; - void onStateChanged(int stateAtomId, const HashableDimensionKey& primaryKey, int oldState, + void onStateChanged(int atomId, const HashableDimensionKey& primaryKey, int oldState, int newState) { updates.emplace_back(primaryKey, newState); } @@ -82,6 +82,7 @@ std::shared_ptr<LogEvent> buildOverlayEvent(int uid, const std::string& packageN return event; } +// Incorrect event - missing fields std::shared_ptr<LogEvent> buildIncorrectOverlayEvent(int uid, const std::string& packageName, int state) { std::shared_ptr<LogEvent> event = std::make_shared<LogEvent>(android::util::OVERLAY_STATE_CHANGED, 1000 /*timestamp*/); @@ -91,6 +92,18 @@ std::shared_ptr<LogEvent> buildIncorrectOverlayEvent(int uid, const std::string& event->init(); return event; } + +// Incorrect event - exclusive state has wrong type +std::shared_ptr<LogEvent> buildOverlayEventBadStateType(int uid, const std::string& packageName) { + std::shared_ptr<LogEvent> event = + std::make_shared<LogEvent>(android::util::OVERLAY_STATE_CHANGED, 1000 /*timestamp*/); + event->write((int32_t)uid); + event->write(packageName); + event->write(true); + event->write("string"); // exclusive state: string instead of int + event->init(); + return event; +} // END: build event functions. // START: get primary key functions @@ -148,22 +161,22 @@ TEST(StateTrackerTest, TestRegisterListener) { // Register listener to non-existing StateTracker EXPECT_EQ(0, mgr.getStateTrackersCount()); - mgr.registerListener(android::util::SCREEN_STATE_CHANGED, listener1); + EXPECT_TRUE(mgr.registerListener(android::util::SCREEN_STATE_CHANGED, listener1)); EXPECT_EQ(1, mgr.getStateTrackersCount()); EXPECT_EQ(1, mgr.getListenersCount(android::util::SCREEN_STATE_CHANGED)); // Register listener to existing StateTracker - mgr.registerListener(android::util::SCREEN_STATE_CHANGED, listener2); + EXPECT_TRUE(mgr.registerListener(android::util::SCREEN_STATE_CHANGED, listener2)); EXPECT_EQ(1, mgr.getStateTrackersCount()); EXPECT_EQ(2, mgr.getListenersCount(android::util::SCREEN_STATE_CHANGED)); // Register already registered listener to existing StateTracker - mgr.registerListener(android::util::SCREEN_STATE_CHANGED, listener2); + EXPECT_TRUE(mgr.registerListener(android::util::SCREEN_STATE_CHANGED, listener2)); EXPECT_EQ(1, mgr.getStateTrackersCount()); EXPECT_EQ(2, mgr.getListenersCount(android::util::SCREEN_STATE_CHANGED)); // Register listener to non-state atom - mgr.registerListener(android::util::BATTERY_LEVEL_CHANGED, listener2); + EXPECT_FALSE(mgr.registerListener(android::util::BATTERY_LEVEL_CHANGED, listener2)); EXPECT_EQ(1, mgr.getStateTrackersCount()); } @@ -227,12 +240,12 @@ TEST(StateTrackerTest, TestStateChangeNoPrimaryFields) { // check StateTracker was updated by querying for state HashableDimensionKey queryKey = DEFAULT_DIMENSION_KEY; - EXPECT_EQ(2, mgr.getState(android::util::SCREEN_STATE_CHANGED, queryKey)); + EXPECT_EQ(2, mgr.getStateValue(android::util::SCREEN_STATE_CHANGED, queryKey)); } /** * Test StateManager's onLogEvent and StateListener's onStateChanged correctly - * updates listener for states with primary keys. + * updates listener for states with one primary key. */ TEST(StateTrackerTest, TestStateChangeOnePrimaryField) { sp<TestStateListener> listener1 = new TestStateListener(); @@ -240,9 +253,8 @@ TEST(StateTrackerTest, TestStateChangeOnePrimaryField) { mgr.registerListener(android::util::UID_PROCESS_STATE_CHANGED, listener1); // log event - std::shared_ptr<LogEvent> event = buildUidProcessEvent( - 1000, - android::app::ProcessStateEnum::PROCESS_STATE_TOP); // state value: 1002 + std::shared_ptr<LogEvent> event = + buildUidProcessEvent(1000 /* uid */, android::app::ProcessStateEnum::PROCESS_STATE_TOP); mgr.onLogEvent(*event); // check listener was updated @@ -252,23 +264,33 @@ TEST(StateTrackerTest, TestStateChangeOnePrimaryField) { // check StateTracker was updated by querying for state HashableDimensionKey queryKey; - getUidProcessKey(1000, &queryKey); - EXPECT_EQ(1002, mgr.getState(android::util::UID_PROCESS_STATE_CHANGED, queryKey)); + getUidProcessKey(1000 /* uid */, &queryKey); + EXPECT_EQ(1002, mgr.getStateValue(android::util::UID_PROCESS_STATE_CHANGED, queryKey)); } +/** + * Test StateManager's onLogEvent and StateListener's onStateChanged correctly + * updates listener for states with multiple primary keys. + */ TEST(StateTrackerTest, TestStateChangeMultiplePrimaryFields) { sp<TestStateListener> listener1 = new TestStateListener(); StateManager mgr; mgr.registerListener(android::util::OVERLAY_STATE_CHANGED, listener1); // log event - std::shared_ptr<LogEvent> event = buildOverlayEvent(1000, "package1", 1); // state: ENTERED + std::shared_ptr<LogEvent> event = + buildOverlayEvent(1000 /* uid */, "package1", 1); // state: ENTERED mgr.onLogEvent(*event); - // check listener update + // check listener was updated EXPECT_EQ(1, listener1->updates.size()); EXPECT_EQ(1000, listener1->updates[0].mKey.getValues()[0].mValue.int_value); EXPECT_EQ(1, listener1->updates[0].mState); + + // check StateTracker was updated by querying for state + HashableDimensionKey queryKey; + getOverlayKey(1000 /* uid */, "package1", &queryKey); + EXPECT_EQ(1, mgr.getStateValue(android::util::OVERLAY_STATE_CHANGED, queryKey)); } /** @@ -282,11 +304,14 @@ TEST(StateTrackerTest, TestStateChangeEventError) { mgr.registerListener(android::util::OVERLAY_STATE_CHANGED, listener1); // log event - std::shared_ptr<LogEvent> event = - buildIncorrectOverlayEvent(1000, "package1", 1); // state: ENTERED - mgr.onLogEvent(*event); + std::shared_ptr<LogEvent> event1 = + buildIncorrectOverlayEvent(1000 /* uid */, "package1", 1 /* state */); + std::shared_ptr<LogEvent> event2 = buildOverlayEventBadStateType(1001 /* uid */, "package2"); - // check listener update + // check listener was updated + mgr.onLogEvent(*event1); + EXPECT_EQ(0, listener1->updates.size()); + mgr.onLogEvent(*event2); EXPECT_EQ(0, listener1->updates.size()); } @@ -301,18 +326,19 @@ TEST(StateTrackerTest, TestStateQuery) { std::shared_ptr<LogEvent> event1 = buildUidProcessEvent( 1000, - android::app::ProcessStateEnum::PROCESS_STATE_TOP); // state value: 1002 + android::app::ProcessStateEnum::PROCESS_STATE_TOP); // state value: 1002 std::shared_ptr<LogEvent> event2 = buildUidProcessEvent( 1001, - android::app::ProcessStateEnum::PROCESS_STATE_FOREGROUND_SERVICE); // state value: 1003 + android::app::ProcessStateEnum::PROCESS_STATE_FOREGROUND_SERVICE); // state value: + // 1003 std::shared_ptr<LogEvent> event3 = buildUidProcessEvent( 1002, - android::app::ProcessStateEnum::PROCESS_STATE_PERSISTENT); // state value: 1000 + android::app::ProcessStateEnum::PROCESS_STATE_PERSISTENT); // state value: 1000 std::shared_ptr<LogEvent> event4 = buildUidProcessEvent( 1001, - android::app::ProcessStateEnum::PROCESS_STATE_TOP); // state value: 1002 + android::app::ProcessStateEnum::PROCESS_STATE_TOP); // state value: 1002 std::shared_ptr<LogEvent> event5 = - buildScreenEvent(android::view::DisplayStateEnum::DISPLAY_STATE_ON); // state value: + buildScreenEvent(android::view::DisplayStateEnum::DISPLAY_STATE_ON); std::shared_ptr<LogEvent> event6 = buildOverlayEvent(1000, "package1", 1); std::shared_ptr<LogEvent> event7 = buildOverlayEvent(1000, "package2", 2); @@ -327,25 +353,25 @@ TEST(StateTrackerTest, TestStateQuery) { // Query for UidProcessState of uid 1001 HashableDimensionKey queryKey1; getUidProcessKey(1001, &queryKey1); - EXPECT_EQ(1003, mgr.getState(android::util::UID_PROCESS_STATE_CHANGED, queryKey1)); + EXPECT_EQ(1003, mgr.getStateValue(android::util::UID_PROCESS_STATE_CHANGED, queryKey1)); // Query for UidProcessState of uid 1004 - not in state map HashableDimensionKey queryKey2; getUidProcessKey(1004, &queryKey2); - EXPECT_EQ(-1, - mgr.getState(android::util::UID_PROCESS_STATE_CHANGED, queryKey2)); // default state + EXPECT_EQ(-1, mgr.getStateValue(android::util::UID_PROCESS_STATE_CHANGED, + queryKey2)); // default state // Query for UidProcessState of uid 1001 - after change in state mgr.onLogEvent(*event4); - EXPECT_EQ(1002, mgr.getState(android::util::UID_PROCESS_STATE_CHANGED, queryKey1)); + EXPECT_EQ(1002, mgr.getStateValue(android::util::UID_PROCESS_STATE_CHANGED, queryKey1)); // Query for ScreenState - EXPECT_EQ(2, mgr.getState(android::util::SCREEN_STATE_CHANGED, DEFAULT_DIMENSION_KEY)); + EXPECT_EQ(2, mgr.getStateValue(android::util::SCREEN_STATE_CHANGED, DEFAULT_DIMENSION_KEY)); // Query for OverlayState of uid 1000, package name "package2" HashableDimensionKey queryKey3; getOverlayKey(1000, "package2", &queryKey3); - EXPECT_EQ(2, mgr.getState(android::util::OVERLAY_STATE_CHANGED, queryKey3)); + EXPECT_EQ(2, mgr.getStateValue(android::util::OVERLAY_STATE_CHANGED, queryKey3)); } } // namespace statsd diff --git a/cmds/statsd/tests/statsd_test_util.cpp b/cmds/statsd/tests/statsd_test_util.cpp index 2c4f3c7692c9..38c22ab5d253 100644 --- a/cmds/statsd/tests/statsd_test_util.cpp +++ b/cmds/statsd/tests/statsd_test_util.cpp @@ -251,6 +251,101 @@ Predicate CreateIsInBackgroundPredicate() { return predicate; } +State CreateScreenState() { + State state; + state.set_id(StringToId("ScreenState")); + state.set_atom_id(29); + return state; +} + +State CreateUidProcessState() { + State state; + state.set_id(StringToId("UidProcessState")); + state.set_atom_id(27); + return state; +} + +State CreateOverlayState() { + State state; + state.set_id(StringToId("OverlayState")); + state.set_atom_id(59); + return state; +} + +State CreateScreenStateWithOnOffMap() { + State state; + state.set_id(StringToId("ScreenStateOnOff")); + state.set_atom_id(29); + + auto map = CreateScreenStateOnOffMap(); + *state.mutable_map() = map; + + return state; +} + +State CreateScreenStateWithInDozeMap() { + State state; + state.set_id(StringToId("ScreenStateInDoze")); + state.set_atom_id(29); + + auto map = CreateScreenStateInDozeMap(); + *state.mutable_map() = map; + + return state; +} + +StateMap_StateGroup CreateScreenStateOnGroup() { + StateMap_StateGroup group; + group.set_group_id(StringToId("SCREEN_ON")); + group.add_value(2); + group.add_value(5); + group.add_value(6); + return group; +} + +StateMap_StateGroup CreateScreenStateOffGroup() { + StateMap_StateGroup group; + group.set_group_id(StringToId("SCREEN_OFF")); + group.add_value(0); + group.add_value(1); + group.add_value(3); + group.add_value(4); + return group; +} + +StateMap CreateScreenStateOnOffMap() { + StateMap map; + *map.add_group() = CreateScreenStateOnGroup(); + *map.add_group() = CreateScreenStateOffGroup(); + return map; +} + +StateMap_StateGroup CreateScreenStateInDozeGroup() { + StateMap_StateGroup group; + group.set_group_id(StringToId("SCREEN_DOZE")); + group.add_value(3); + group.add_value(4); + return group; +} + +StateMap_StateGroup CreateScreenStateNotDozeGroup() { + StateMap_StateGroup group; + group.set_group_id(StringToId("SCREEN_NOT_DOZE")); + group.add_value(0); + group.add_value(1); + group.add_value(2); + group.add_value(5); + group.add_value(6); + return group; +} + +StateMap CreateScreenStateInDozeMap() { + StateMap map; + *map.add_group() = CreateScreenStateInDozeGroup(); + *map.add_group() = CreateScreenStateNotDozeGroup(); + return map; +} + void addPredicateToPredicateCombination(const Predicate& predicate, Predicate* combinationPredicate) { combinationPredicate->mutable_combination()->add_predicate(predicate.id()); diff --git a/cmds/statsd/tests/statsd_test_util.h b/cmds/statsd/tests/statsd_test_util.h index 635c5835333a..c02610540cf0 100644 --- a/cmds/statsd/tests/statsd_test_util.h +++ b/cmds/statsd/tests/statsd_test_util.h @@ -104,6 +104,37 @@ Predicate CreateIsSyncingPredicate(); // Create a Predicate proto for app is in background. Predicate CreateIsInBackgroundPredicate(); +// Create State proto for screen state atom. +State CreateScreenState(); + +// Create State proto for uid process state atom. +State CreateUidProcessState(); + +// Create State proto for overlay state atom. +State CreateOverlayState(); + +State CreateScreenStateWithOnOffMap(); + +State CreateScreenStateWithInDozeMap(); + +// Create StateGroup proto for ScreenState ON group +StateMap_StateGroup CreateScreenStateOnGroup(); + +// Create StateGroup proto for ScreenState OFF group +StateMap_StateGroup CreateScreenStateOffGroup(); + +// Create StateMap proto for ScreenState ON/OFF map +StateMap CreateScreenStateOnOffMap(); + +// Create StateGroup proto for ScreenState IN DOZE group +StateMap_StateGroup CreateScreenStateInDozeGroup(); + +// Create StateGroup proto for ScreenState NOT IN DOZE group +StateMap_StateGroup CreateScreenStateNotDozeGroup(); + +// Create StateMap proto for ScreenState IN DOZE map +StateMap CreateScreenStateInDozeMap(); + // Add a predicate to the predicate combination. void addPredicateToPredicateCombination(const Predicate& predicate, Predicate* combination); @@ -319,4 +350,4 @@ void backfillStartEndTimestampForSkippedBuckets(const int64_t timeBaseNs, T* met } } // namespace statsd } // namespace os -} // namespace android
\ No newline at end of file +} // namespace android diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java index 39fab634f03c..d5bc9b0b213a 100644 --- a/core/java/android/app/ContextImpl.java +++ b/core/java/android/app/ContextImpl.java @@ -2212,9 +2212,9 @@ class ContextImpl extends Context { } @Override - public Context createContextAsUser(UserHandle user) { + public Context createContextAsUser(UserHandle user, @CreatePackageOptions int flags) { try { - return createPackageContextAsUser(getPackageName(), mFlags, user); + return createPackageContextAsUser(getPackageName(), flags, user); } catch (NameNotFoundException e) { throw new IllegalStateException("Own package not found: package=" + getPackageName()); } diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java index 316cab8d600b..58f6741b0724 100644 --- a/core/java/android/app/NotificationManager.java +++ b/core/java/android/app/NotificationManager.java @@ -997,8 +997,13 @@ public class NotificationManager { } /** - * @hide + * <p> + * Gets the currently applied notification policy. If {@link #getCurrentInterruptionFilter} + * is equal to {@link #INTERRUPTION_FILTER_ALL}, then the consolidated notification policy + * will match the default notification policy returned by {@link #getNotificationPolicy}. + * </p> */ + @Nullable public NotificationManager.Policy getConsolidatedNotificationPolicy() { INotificationManager service = getService(); try { diff --git a/core/java/android/app/ResourcesManager.java b/core/java/android/app/ResourcesManager.java index 9e6054c715d6..68ab89cfbd01 100644 --- a/core/java/android/app/ResourcesManager.java +++ b/core/java/android/app/ResourcesManager.java @@ -884,21 +884,24 @@ public class ResourcesManager { * @param activityToken optional token to clean up Activity resources */ private void cleanupReferences(IBinder activityToken) { - if (activityToken != null) { - ActivityResources activityResources = mActivityResourceReferences.get(activityToken); - if (activityResources != null) { - ArrayUtils.unstableRemoveIf(activityResources.activityResources, - sEmptyReferencePredicate); + synchronized (this) { + if (activityToken != null) { + ActivityResources activityResources = mActivityResourceReferences.get( + activityToken); + if (activityResources != null) { + ArrayUtils.unstableRemoveIf(activityResources.activityResources, + sEmptyReferencePredicate); + } + } else { + ArrayUtils.unstableRemoveIf(mResourceReferences, sEmptyReferencePredicate); } - } else { - ArrayUtils.unstableRemoveIf(mResourceReferences, sEmptyReferencePredicate); - } - for (int index = mResourcesWithLoaders.size() - 1; index >= 0; index--) { - ResourcesWithLoaders resourcesWithLoaders = mResourcesWithLoaders.get(index); - Resources resources = resourcesWithLoaders.resources(); - if (resources == null) { - mResourcesWithLoaders.remove(index); + for (int index = mResourcesWithLoaders.size() - 1; index >= 0; index--) { + ResourcesWithLoaders resourcesWithLoaders = mResourcesWithLoaders.get(index); + Resources resources = resourcesWithLoaders.resources(); + if (resources == null) { + mResourcesWithLoaders.remove(index); + } } } } diff --git a/core/java/android/app/admin/PasswordMetrics.java b/core/java/android/app/admin/PasswordMetrics.java index 992985528fca..464f75c3d0b7 100644 --- a/core/java/android/app/admin/PasswordMetrics.java +++ b/core/java/android/app/admin/PasswordMetrics.java @@ -27,9 +27,6 @@ import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_NUMERIC; import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_SOMETHING; import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED; -import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PASSWORD; -import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PATTERN; - import android.annotation.IntDef; import android.annotation.NonNull; import android.app.admin.DevicePolicyManager.PasswordComplexity; @@ -37,8 +34,7 @@ import android.os.Parcel; import android.os.Parcelable; import com.android.internal.annotations.VisibleForTesting; -import com.android.internal.util.Preconditions; -import com.android.internal.widget.LockPatternUtils.CredentialType; +import com.android.internal.widget.LockscreenCredential; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -226,21 +222,21 @@ public class PasswordMetrics implements Parcelable { }; /** - * Returnsthe {@code PasswordMetrics} for a given credential. + * Returns the {@code PasswordMetrics} for a given credential. * * If the credential is a pin or a password, equivalent to {@link #computeForPassword(byte[])}. * {@code credential} cannot be null when {@code type} is * {@link com.android.internal.widget.LockPatternUtils#CREDENTIAL_TYPE_PASSWORD}. */ - public static PasswordMetrics computeForCredential( - @CredentialType int type, byte[] credential) { - if (type == CREDENTIAL_TYPE_PASSWORD) { - Preconditions.checkNotNull(credential, "credential cannot be null"); - return PasswordMetrics.computeForPassword(credential); - } else if (type == CREDENTIAL_TYPE_PATTERN) { + public static PasswordMetrics computeForCredential(LockscreenCredential credential) { + if (credential.isPassword()) { + return PasswordMetrics.computeForPassword(credential.getCredential()); + } else if (credential.isPattern()) { return new PasswordMetrics(PASSWORD_QUALITY_SOMETHING); - } else /* if (type == CREDENTIAL_TYPE_NONE) */ { + } else if (credential.isNone()) { return new PasswordMetrics(PASSWORD_QUALITY_UNSPECIFIED); + } else { + throw new IllegalArgumentException("Unknown credential type " + credential.getType()); } } diff --git a/core/java/android/app/slice/SliceManager.java b/core/java/android/app/slice/SliceManager.java index 955093d3380e..90ecce2a2170 100644 --- a/core/java/android/app/slice/SliceManager.java +++ b/core/java/android/app/slice/SliceManager.java @@ -390,6 +390,8 @@ public class SliceManager { } Bundle extras = new Bundle(); extras.putParcelable(SliceProvider.EXTRA_INTENT, intent); + extras.putParcelableArrayList(SliceProvider.EXTRA_SUPPORTED_SPECS, + new ArrayList<>(supportedSpecs)); final Bundle res = provider.call(SliceProvider.METHOD_MAP_INTENT, null, extras); if (res == null) { return null; diff --git a/core/java/android/app/timedetector/TimeSignal.java b/core/java/android/app/timedetector/TimeSignal.java index da21794cd649..b49426000d88 100644 --- a/core/java/android/app/timedetector/TimeSignal.java +++ b/core/java/android/app/timedetector/TimeSignal.java @@ -56,8 +56,7 @@ public final class TimeSignal implements Parcelable { private static TimeSignal createFromParcel(Parcel in) { String sourceId = in.readString(); - TimestampedValue<Long> utcTime = - TimestampedValue.readFromParcel(in, null /* classLoader */, Long.class); + TimestampedValue<Long> utcTime = in.readParcelable(null /* classLoader */); return new TimeSignal(sourceId, utcTime); } @@ -69,7 +68,7 @@ public final class TimeSignal implements Parcelable { @Override public void writeToParcel(@NonNull Parcel dest, int flags) { dest.writeString(mSourceId); - TimestampedValue.writeToParcel(dest, mUtcTime); + dest.writeParcelable(mUtcTime, 0); } @NonNull diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index 227684b1bd14..e7e278f431ba 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -5244,7 +5244,7 @@ public abstract class Context { @SystemApi @TestApi @NonNull - public Context createContextAsUser(@NonNull UserHandle user) { + public Context createContextAsUser(@NonNull UserHandle user, @CreatePackageOptions int flags) { if (Build.IS_ENG) { throw new IllegalStateException("createContextAsUser not overridden!"); } diff --git a/core/java/android/content/ContextWrapper.java b/core/java/android/content/ContextWrapper.java index f7cd51e7ffbc..7993ea192424 100644 --- a/core/java/android/content/ContextWrapper.java +++ b/core/java/android/content/ContextWrapper.java @@ -885,8 +885,8 @@ public class ContextWrapper extends Context { /** @hide */ @Override - public Context createContextAsUser(UserHandle user) { - return mBase.createContextAsUser(user); + public Context createContextAsUser(UserHandle user, @CreatePackageOptions int flags) { + return mBase.createContextAsUser(user, flags); } /** @hide */ diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index f302d5949feb..7509065a34b1 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -2858,6 +2858,14 @@ public abstract class PackageManager { /** * Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}: + * The device does not have slices implementation. + * @hide + */ + @SdkConstant(SdkConstantType.FEATURE) + public static final String FEATURE_SLICES_DISABLED = "android.software.slices_disabled"; + + /** + * Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}: * The device supports device-unique Keystore attestations. Only available on devices that * also support {@link #FEATURE_STRONGBOX_KEYSTORE}, and can only be used by device owner * apps (see {@link android.app.admin.DevicePolicyManager#generateKeyPair}). diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java index 0b157fa3bb1e..cf21e9665c71 100644 --- a/core/java/android/content/pm/PackageParser.java +++ b/core/java/android/content/pm/PackageParser.java @@ -577,8 +577,6 @@ public class PackageParser { */ public interface Callback { boolean hasFeature(String feature); - String[] getOverlayPaths(String targetPackageName, String targetPath); - String[] getOverlayApks(String targetPackageName); } /** @@ -595,14 +593,6 @@ public class PackageParser { @Override public boolean hasFeature(String feature) { return mPm.hasSystemFeature(feature); } - - @Override public String[] getOverlayPaths(String targetPackageName, String targetPath) { - return null; - } - - @Override public String[] getOverlayApks(String targetPackageName) { - return null; - } } /** @@ -1195,19 +1185,7 @@ public class PackageParser { } final byte[] bytes = IoUtils.readFileAsByteArray(cacheFile.getAbsolutePath()); - Package p = fromCacheEntry(bytes); - if (mCallback != null) { - String[] overlayApks = mCallback.getOverlayApks(p.packageName); - if (overlayApks != null && overlayApks.length > 0) { - for (String overlayApk : overlayApks) { - // If a static RRO is updated, return null. - if (!isCacheUpToDate(new File(overlayApk), cacheFile)) { - return null; - } - } - } - } - return p; + return fromCacheEntry(bytes); } catch (Throwable e) { Slog.w(TAG, "Error reading package cache: ", e); @@ -1381,7 +1359,7 @@ public class PackageParser { final Resources res = new Resources(assets, mMetrics, null); final String[] outError = new String[1]; - final Package pkg = parseBaseApk(apkPath, res, parser, flags, outError); + final Package pkg = parseBaseApk(res, parser, flags, outError); if (pkg == null) { throw new PackageParserException(mParseError, apkPath + " (at " + parser.getPositionDescription() + "): " + outError[0]); @@ -1944,7 +1922,6 @@ public class PackageParser { * need to consider whether they should be supported by split APKs and child * packages. * - * @param apkPath The package apk file path * @param res The resources from which to resolve values * @param parser The manifest parser * @param flags Flags how to parse @@ -1954,8 +1931,7 @@ public class PackageParser { * @throws XmlPullParserException * @throws IOException */ - @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) - private Package parseBaseApk(String apkPath, Resources res, XmlResourceParser parser, int flags, + private Package parseBaseApk(Resources res, XmlResourceParser parser, int flags, String[] outError) throws XmlPullParserException, IOException { final String splitName; final String pkgName; @@ -1975,15 +1951,6 @@ public class PackageParser { return null; } - if (mCallback != null) { - String[] overlayPaths = mCallback.getOverlayPaths(pkgName, apkPath); - if (overlayPaths != null && overlayPaths.length > 0) { - for (String overlayPath : overlayPaths) { - res.getAssets().addOverlayPath(overlayPath); - } - } - } - final Package pkg = new Package(pkgName); TypedArray sa = res.obtainAttributes(parser, diff --git a/core/java/android/content/pm/PermissionInfo.java b/core/java/android/content/pm/PermissionInfo.java index c77c53f387e2..aa6f58e82bcf 100644 --- a/core/java/android/content/pm/PermissionInfo.java +++ b/core/java/android/content/pm/PermissionInfo.java @@ -248,6 +248,17 @@ public class PermissionInfo extends PackageItemInfo implements Parcelable { @TestApi public static final int PROTECTION_FLAG_TELEPHONY = 0x400000; + /** + * Additional flag for {@link #protectionLevel}, corresponding + * to the <code>wifi</code> value of + * {@link android.R.attr#protectionLevel}. + * + * @hide + */ + @SystemApi + @TestApi + public static final int PROTECTION_FLAG_WIFI = 0x800000; + /** @hide */ @IntDef(flag = true, prefix = { "PROTECTION_FLAG_" }, value = { PROTECTION_FLAG_PRIVILEGED, @@ -270,6 +281,7 @@ public class PermissionInfo extends PackageItemInfo implements Parcelable { PROTECTION_FLAG_INCIDENT_REPORT_APPROVER, PROTECTION_FLAG_APP_PREDICTOR, PROTECTION_FLAG_TELEPHONY, + PROTECTION_FLAG_WIFI, }) @Retention(RetentionPolicy.SOURCE) public @interface ProtectionFlags {} @@ -516,6 +528,9 @@ public class PermissionInfo extends PackageItemInfo implements Parcelable { if ((level & PermissionInfo.PROTECTION_FLAG_TELEPHONY) != 0) { protLevel += "|telephony"; } + if ((level & PermissionInfo.PROTECTION_FLAG_WIFI) != 0) { + protLevel += "|wifi"; + } return protLevel; } diff --git a/core/java/android/net/INetworkPolicyListener.aidl b/core/java/android/net/INetworkPolicyListener.aidl index 106b7be5c8d3..fe9141cb6a20 100644 --- a/core/java/android/net/INetworkPolicyListener.aidl +++ b/core/java/android/net/INetworkPolicyListener.aidl @@ -15,6 +15,7 @@ */ package android.net; +import android.telephony.SubscriptionPlan; /** {@hide} */ oneway interface INetworkPolicyListener { @@ -22,5 +23,6 @@ oneway interface INetworkPolicyListener { void onMeteredIfacesChanged(in String[] meteredIfaces); void onRestrictBackgroundChanged(boolean restrictBackground); void onUidPoliciesChanged(int uid, int uidPolicies); - void onSubscriptionOverride(int subId, int overrideMask, int overrideValue, long networkTypeMask); + void onSubscriptionOverride(int subId, int overrideMask, int overrideValue); + void onSubscriptionPlansChanged(int subId, in SubscriptionPlan[] plans); } diff --git a/core/java/android/net/INetworkPolicyManager.aidl b/core/java/android/net/INetworkPolicyManager.aidl index 90327663e34b..385cb1d68b57 100644 --- a/core/java/android/net/INetworkPolicyManager.aidl +++ b/core/java/android/net/INetworkPolicyManager.aidl @@ -76,7 +76,7 @@ interface INetworkPolicyManager { SubscriptionPlan[] getSubscriptionPlans(int subId, String callingPackage); void setSubscriptionPlans(int subId, in SubscriptionPlan[] plans, String callingPackage); String getSubscriptionPlansOwner(int subId); - void setSubscriptionOverride(int subId, int overrideMask, int overrideValue, long networkTypeMask, long timeoutMillis, String callingPackage); + void setSubscriptionOverride(int subId, int overrideMask, int overrideValue, long timeoutMillis, String callingPackage); void factoryReset(String subscriber); diff --git a/core/java/android/net/NetworkPolicyManager.java b/core/java/android/net/NetworkPolicyManager.java index 628dcd2691cf..9150aae14049 100644 --- a/core/java/android/net/NetworkPolicyManager.java +++ b/core/java/android/net/NetworkPolicyManager.java @@ -31,6 +31,7 @@ import android.net.wifi.WifiInfo; import android.os.Build; import android.os.RemoteException; import android.os.UserHandle; +import android.telephony.SubscriptionPlan; import android.util.DebugUtils; import android.util.Pair; import android.util.Range; @@ -380,7 +381,8 @@ public class NetworkPolicyManager { @Override public void onMeteredIfacesChanged(String[] meteredIfaces) { } @Override public void onRestrictBackgroundChanged(boolean restrictBackground) { } @Override public void onUidPoliciesChanged(int uid, int uidPolicies) { } - @Override public void onSubscriptionOverride(int subId, int overrideMask, int overrideValue, - long networkTypeMask) { } + @Override public void onSubscriptionOverride(int subId, int overrideMask, + int overrideValue) { } + @Override public void onSubscriptionPlansChanged(int subId, SubscriptionPlan[] plans) { } } } diff --git a/core/java/android/provider/DocumentsContract.java b/core/java/android/provider/DocumentsContract.java index fd81178d2cfb..eb09930005b5 100644 --- a/core/java/android/provider/DocumentsContract.java +++ b/core/java/android/provider/DocumentsContract.java @@ -363,15 +363,22 @@ public final class DocumentsContract { * <p> * Type: INTEGER (int) * - * @see #FLAG_SUPPORTS_WRITE - * @see #FLAG_SUPPORTS_DELETE - * @see #FLAG_SUPPORTS_THUMBNAIL + * @see #FLAG_DIR_BLOCKS_TREE * @see #FLAG_DIR_PREFERS_GRID * @see #FLAG_DIR_PREFERS_LAST_MODIFIED - * @see #FLAG_VIRTUAL_DOCUMENT + * @see #FLAG_DIR_SUPPORTS_CREATE + * @see #FLAG_PARTIAL * @see #FLAG_SUPPORTS_COPY + * @see #FLAG_SUPPORTS_DELETE + * @see #FLAG_SUPPORTS_METADATA * @see #FLAG_SUPPORTS_MOVE * @see #FLAG_SUPPORTS_REMOVE + * @see #FLAG_SUPPORTS_RENAME + * @see #FLAG_SUPPORTS_SETTINGS + * @see #FLAG_SUPPORTS_THUMBNAIL + * @see #FLAG_SUPPORTS_WRITE + * @see #FLAG_VIRTUAL_DOCUMENT + * @see #FLAG_WEB_LINKABLE */ public static final String COLUMN_FLAGS = "flags"; @@ -542,6 +549,23 @@ public final class DocumentsContract { * @see DocumentsContract#getDocumentMetadata(ContentInterface, Uri) */ public static final int FLAG_SUPPORTS_METADATA = 1 << 14; + + /** + * Flag indicating that a document is a directory that wants to block itself + * from being selected when the user launches an {@link Intent#ACTION_OPEN_DOCUMENT_TREE} + * intent. Only valid when {@link #COLUMN_MIME_TYPE} is {@link #MIME_TYPE_DIR}. + * <p> + * Note that this flag <em>only</em> applies to the single directory to which it is + * applied. It does <em>not</em> block the user from selecting either a parent or + * child directory during an {@link Intent#ACTION_OPEN_DOCUMENT_TREE} request. + * In particular, the only way to guarantee that a specific directory can never + * be granted via an {@link Intent#ACTION_OPEN_DOCUMENT_TREE} request is to ensure + * that both it and <em>all of its parent directories</em> have set this flag. + * + * @see Intent#ACTION_OPEN_DOCUMENT_TREE + * @see #COLUMN_FLAGS + */ + public static final int FLAG_DIR_BLOCKS_TREE = 1 << 15; } /** diff --git a/core/java/android/provider/MediaStore.java b/core/java/android/provider/MediaStore.java index 493f9a2ce123..a1333df13820 100644 --- a/core/java/android/provider/MediaStore.java +++ b/core/java/android/provider/MediaStore.java @@ -39,7 +39,6 @@ import android.content.Context; import android.content.Intent; import android.content.UriPermission; import android.database.Cursor; -import android.database.DatabaseUtils; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.ImageDecoder; @@ -70,15 +69,19 @@ import android.util.Log; import com.android.internal.annotations.GuardedBy; +import libcore.util.HexEncoding; + import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import java.text.Collator; import java.util.ArrayList; import java.util.Collection; import java.util.List; +import java.util.Locale; import java.util.Objects; import java.util.Set; import java.util.regex.Pattern; @@ -2059,7 +2062,17 @@ public final class MediaStore { /** * A non human readable key calculated from the TITLE, used for * searching, sorting and grouping + * + * @see Audio#keyFor(String) + * @deprecated These keys are generated using + * {@link java.util.Locale#ROOT}, which means they don't + * reflect locale-specific sorting preferences. To apply + * locale-specific sorting preferences, use + * {@link ContentResolver#QUERY_ARG_SQL_SORT_ORDER} with + * {@code COLLATE LOCALIZED}, or + * {@link ContentResolver#QUERY_ARG_SORT_LOCALE}. */ + @Deprecated @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true) public static final String TITLE_KEY = "title_key"; @@ -2104,7 +2117,17 @@ public final class MediaStore { /** * A non human readable key calculated from the ARTIST, used for * searching, sorting and grouping + * + * @see Audio#keyFor(String) + * @deprecated These keys are generated using + * {@link java.util.Locale#ROOT}, which means they don't + * reflect locale-specific sorting preferences. To apply + * locale-specific sorting preferences, use + * {@link ContentResolver#QUERY_ARG_SQL_SORT_ORDER} with + * {@code COLLATE LOCALIZED}, or + * {@link ContentResolver#QUERY_ARG_SORT_LOCALE}. */ + @Deprecated @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true) public static final String ARTIST_KEY = "artist_key"; @@ -2129,7 +2152,17 @@ public final class MediaStore { /** * A non human readable key calculated from the ALBUM, used for * searching, sorting and grouping + * + * @see Audio#keyFor(String) + * @deprecated These keys are generated using + * {@link java.util.Locale#ROOT}, which means they don't + * reflect locale-specific sorting preferences. To apply + * locale-specific sorting preferences, use + * {@link ContentResolver#QUERY_ARG_SQL_SORT_ORDER} with + * {@code COLLATE LOCALIZED}, or + * {@link ContentResolver#QUERY_ARG_SORT_LOCALE}. */ + @Deprecated @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true) public static final String ALBUM_KEY = "album_key"; @@ -2186,91 +2219,89 @@ public final class MediaStore { public static final String IS_AUDIOBOOK = "is_audiobook"; /** - * The genre of the audio file, if any - * Does not exist in the database - only used by the media scanner for inserts. - * @hide + * The id of the genre the audio file is from, if any */ - @Deprecated - // @Column(Cursor.FIELD_TYPE_STRING) + @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true) + public static final String GENRE_ID = "genre_id"; + + /** + * The genre of the audio file, if any. + */ + @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true) public static final String GENRE = "genre"; /** - * The resource URI of a localized title, if any + * A non human readable key calculated from the GENRE, used for + * searching, sorting and grouping + * + * @see Audio#keyFor(String) + * @deprecated These keys are generated using + * {@link java.util.Locale#ROOT}, which means they don't + * reflect locale-specific sorting preferences. To apply + * locale-specific sorting preferences, use + * {@link ContentResolver#QUERY_ARG_SQL_SORT_ORDER} with + * {@code COLLATE LOCALIZED}, or + * {@link ContentResolver#QUERY_ARG_SORT_LOCALE}. + */ + @Deprecated + @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true) + public static final String GENRE_KEY = "genre_key"; + + /** + * The resource URI of a localized title, if any. + * <p> * Conforms to this pattern: - * Scheme: {@link ContentResolver.SCHEME_ANDROID_RESOURCE} - * Authority: Package Name of ringtone title provider - * First Path Segment: Type of resource (must be "string") - * Second Path Segment: Resource ID of title - * @hide + * <ul> + * <li>Scheme: {@link ContentResolver#SCHEME_ANDROID_RESOURCE} + * <li>Authority: Package Name of ringtone title provider + * <li>First Path Segment: Type of resource (must be "string") + * <li>Second Path Segment: Resource ID of title + * </ul> */ @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true) public static final String TITLE_RESOURCE_URI = "title_resource_uri"; } + private static final Pattern PATTERN_TRIM_BEFORE = Pattern.compile( + "(?i)(^(the|an|a) |,\\s*(the|an|a)$|[^\\w\\s]|^\\s+|\\s+$)"); + private static final Pattern PATTERN_TRIM_AFTER = Pattern.compile( + "(^(00)+|(00)+$)"); + /** - * Converts a name to a "key" that can be used for grouping, sorting - * and searching. - * The rules that govern this conversion are: - * - remove 'special' characters like ()[]'!?., - * - remove leading/trailing spaces - * - convert everything to lowercase - * - remove leading "the ", "an " and "a " - * - remove trailing ", the|an|a" - * - remove accents. This step leaves us with CollationKey data, - * which is not human readable + * Converts a user-visible string into a "key" that can be used for + * grouping, sorting, and searching. * - * @param name The artist or album name to convert - * @return The "key" for the given name. - */ - public static String keyFor(String name) { - if (name != null) { - boolean sortfirst = false; - if (name.equals(UNKNOWN_STRING)) { - return "\001"; - } - // Check if the first character is \001. We use this to - // force sorting of certain special files, like the silent ringtone. - if (name.startsWith("\001")) { - sortfirst = true; - } - name = name.trim().toLowerCase(); - if (name.startsWith("the ")) { - name = name.substring(4); - } - if (name.startsWith("an ")) { - name = name.substring(3); - } - if (name.startsWith("a ")) { - name = name.substring(2); - } - if (name.endsWith(", the") || name.endsWith(",the") || - name.endsWith(", an") || name.endsWith(",an") || - name.endsWith(", a") || name.endsWith(",a")) { - name = name.substring(0, name.lastIndexOf(',')); - } - name = name.replaceAll("[\\[\\]\\(\\)\"'.,?!]", "").trim(); - if (name.length() > 0) { - // Insert a separator between the characters to avoid - // matches on a partial character. If we ever change - // to start-of-word-only matches, this can be removed. - StringBuilder b = new StringBuilder(); - b.append('.'); - int nl = name.length(); - for (int i = 0; i < nl; i++) { - b.append(name.charAt(i)); - b.append('.'); - } - name = b.toString(); - String key = DatabaseUtils.getCollationKey(name); - if (sortfirst) { - key = "\001" + key; - } - return key; - } else { - return ""; - } + * @return Opaque token that should not be parsed or displayed to users. + * @deprecated These keys are generated using + * {@link java.util.Locale#ROOT}, which means they don't + * reflect locale-specific sorting preferences. To apply + * locale-specific sorting preferences, use + * {@link ContentResolver#QUERY_ARG_SQL_SORT_ORDER} with + * {@code COLLATE LOCALIZED}, or + * {@link ContentResolver#QUERY_ARG_SORT_LOCALE}. + */ + @Deprecated + public static @Nullable String keyFor(@Nullable String name) { + if (TextUtils.isEmpty(name)) return null; + + if (UNKNOWN_STRING.equals(name)) { + return "01"; + } + + final boolean sortFirst = name.startsWith("\001"); + + name = PATTERN_TRIM_BEFORE.matcher(name).replaceAll(""); + if (TextUtils.isEmpty(name)) return null; + + final Collator c = Collator.getInstance(Locale.ROOT); + c.setStrength(Collator.PRIMARY); + name = HexEncoding.encodeToString(c.getCollationKey(name).toByteArray(), false); + + name = PATTERN_TRIM_AFTER.matcher(name).replaceAll(""); + if (sortFirst) { + name = "01" + name; } - return null; + return name; } public static final class Media implements AudioColumns { @@ -2632,7 +2663,17 @@ public final class MediaStore { /** * A non human readable key calculated from the ARTIST, used for * searching, sorting and grouping + * + * @see Audio#keyFor(String) + * @deprecated These keys are generated using + * {@link java.util.Locale#ROOT}, which means they don't + * reflect locale-specific sorting preferences. To apply + * locale-specific sorting preferences, use + * {@link ContentResolver#QUERY_ARG_SQL_SORT_ORDER} with + * {@code COLLATE LOCALIZED}, or + * {@link ContentResolver#QUERY_ARG_SORT_LOCALE}. */ + @Deprecated @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true) public static final String ARTIST_KEY = "artist_key"; @@ -2736,6 +2777,23 @@ public final class MediaStore { public static final String ARTIST = "artist"; /** + * A non human readable key calculated from the ARTIST, used for + * searching, sorting and grouping + * + * @see Audio#keyFor(String) + * @deprecated These keys are generated using + * {@link java.util.Locale#ROOT}, which means they don't + * reflect locale-specific sorting preferences. To apply + * locale-specific sorting preferences, use + * {@link ContentResolver#QUERY_ARG_SQL_SORT_ORDER} with + * {@code COLLATE LOCALIZED}, or + * {@link ContentResolver#QUERY_ARG_SORT_LOCALE}. + */ + @Deprecated + @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true) + public static final String ARTIST_KEY = "artist_key"; + + /** * The number of songs on this album */ @Column(value = Cursor.FIELD_TYPE_INTEGER, readOnly = true) @@ -2770,7 +2828,17 @@ public final class MediaStore { /** * A non human readable key calculated from the ALBUM, used for * searching, sorting and grouping + * + * @see Audio#keyFor(String) + * @deprecated These keys are generated using + * {@link java.util.Locale#ROOT}, which means they don't + * reflect locale-specific sorting preferences. To apply + * locale-specific sorting preferences, use + * {@link ContentResolver#QUERY_ARG_SQL_SORT_ORDER} with + * {@code COLLATE LOCALIZED}, or + * {@link ContentResolver#QUERY_ARG_SORT_LOCALE}. */ + @Deprecated @Column(value = Cursor.FIELD_TYPE_STRING, readOnly = true) public static final String ALBUM_KEY = "album_key"; diff --git a/core/java/android/util/DebugUtils.java b/core/java/android/util/DebugUtils.java index 20e0d14ac14d..bc5edf89b4b0 100644 --- a/core/java/android/util/DebugUtils.java +++ b/core/java/android/util/DebugUtils.java @@ -255,7 +255,7 @@ public class DebugUtils { if (value == 0 && flagsWasZero) { return constNameWithoutPrefix(prefix, field); } - if ((flags & value) == value) { + if (value != 0 && (flags & value) == value) { flags &= ~value; res.append(constNameWithoutPrefix(prefix, field)).append('|'); } diff --git a/core/java/android/util/TimestampedValue.java b/core/java/android/util/TimestampedValue.java index 1289e4db0743..45056730b08b 100644 --- a/core/java/android/util/TimestampedValue.java +++ b/core/java/android/util/TimestampedValue.java @@ -19,6 +19,7 @@ package android.util; import android.annotation.NonNull; import android.annotation.Nullable; import android.os.Parcel; +import android.os.Parcelable; import android.os.SystemClock; import java.util.Objects; @@ -30,14 +31,14 @@ import java.util.Objects; * If a suitable clock is used the reference time can be used to identify the age of a value or * ordering between values. * - * <p>To read and write a timestamped value from / to a Parcel see - * {@link #readFromParcel(Parcel, ClassLoader, Class)} and - * {@link #writeToParcel(Parcel, TimestampedValue)}. + * <p>This class implements {@link Parcelable} for convenience but instances will only actually be + * parcelable if the value type held is {@code null}, {@link Parcelable}, or one of the other types + * supported by {@link Parcel#writeValue(Object)} / {@link Parcel#readValue(ClassLoader)}. * * @param <T> the type of the value with an associated timestamp * @hide */ -public final class TimestampedValue<T> { +public final class TimestampedValue<T> implements Parcelable { private final long mReferenceTimeMillis; private final T mValue; @@ -81,57 +82,43 @@ public final class TimestampedValue<T> { } /** - * Read a {@link TimestampedValue} from a parcel that was stored using - * {@link #writeToParcel(Parcel, TimestampedValue)}. - * - * <p>The marshalling/unmarshalling of the value relies upon {@link Parcel#writeValue(Object)} - * and {@link Parcel#readValue(ClassLoader)} and so this method can only be used with types - * supported by those methods. - * - * @param in the Parcel to read from - * @param classLoader the ClassLoader to pass to {@link Parcel#readValue(ClassLoader)} - * @param valueClass the expected type of the value, typically the same as {@code <T>} but can - * also be a subclass - * @throws RuntimeException if the value read is not compatible with {@code valueClass} or the - * object could not be read - */ - @SuppressWarnings("unchecked") - @NonNull - public static <T> TimestampedValue<T> readFromParcel( - @NonNull Parcel in, @Nullable ClassLoader classLoader, Class<? extends T> valueClass) { - long referenceTimeMillis = in.readLong(); - T value = (T) in.readValue(classLoader); - // Equivalent to static code: if (!(value.getClass() instanceof {valueClass})) { - if (value != null && !valueClass.isAssignableFrom(value.getClass())) { - throw new RuntimeException("Value was of type " + value.getClass() - + " is not assignable to " + valueClass); - } - return new TimestampedValue<>(referenceTimeMillis, value); - } - - /** - * Write a {@link TimestampedValue} to a parcel so that it can be read using - * {@link #readFromParcel(Parcel, ClassLoader, Class)}. - * - * <p>The marshalling/unmarshalling of the value relies upon {@link Parcel#writeValue(Object)} - * and {@link Parcel#readValue(ClassLoader)} and so this method can only be used with types - * supported by those methods. - * - * @param dest the Parcel - * @param timestampedValue the value - * @throws RuntimeException if the value could not be written to the Parcel - */ - public static void writeToParcel( - @NonNull Parcel dest, @NonNull TimestampedValue<?> timestampedValue) { - dest.writeLong(timestampedValue.mReferenceTimeMillis); - dest.writeValue(timestampedValue.mValue); - } - - /** * Returns the difference in milliseconds between two instance's reference times. */ public static long referenceTimeDifference( @NonNull TimestampedValue<?> one, @NonNull TimestampedValue<?> two) { return one.mReferenceTimeMillis - two.mReferenceTimeMillis; } + + public static final @NonNull Parcelable.Creator<TimestampedValue<?>> CREATOR = + new Parcelable.ClassLoaderCreator<TimestampedValue<?>>() { + + @Override + public TimestampedValue<?> createFromParcel(@NonNull Parcel source) { + return createFromParcel(source, null); + } + + @Override + public TimestampedValue<?> createFromParcel( + @NonNull Parcel source, @Nullable ClassLoader classLoader) { + long referenceTimeMillis = source.readLong(); + Object value = source.readValue(classLoader); + return new TimestampedValue<>(referenceTimeMillis, value); + } + + @Override + public TimestampedValue[] newArray(int size) { + return new TimestampedValue[size]; + } + }; + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + dest.writeLong(mReferenceTimeMillis); + dest.writeValue(mValue); + } } diff --git a/core/java/android/view/IRecentsAnimationController.aidl b/core/java/android/view/IRecentsAnimationController.aidl index 955be8d40c47..762366eb6295 100644 --- a/core/java/android/view/IRecentsAnimationController.aidl +++ b/core/java/android/view/IRecentsAnimationController.aidl @@ -120,4 +120,10 @@ interface IRecentsAnimationController { * @see IRecentsAnimationRunner#onCancelled */ void setDeferCancelUntilNextTransition(boolean defer, boolean screenshot); + + /** + * Sets a state for controller to decide which surface is the destination when the recents + * animation is cancelled through fail safe mechanism. + */ + void setWillFinishToHome(boolean willFinishToHome); } diff --git a/core/java/android/view/IWindow.aidl b/core/java/android/view/IWindow.aidl index 4b872d3ad758..8bf99ec4f251 100644 --- a/core/java/android/view/IWindow.aidl +++ b/core/java/android/view/IWindow.aidl @@ -81,6 +81,14 @@ oneway interface IWindow { */ void showInsets(int types, boolean fromIme); + /** + * Called when a set of insets source window should be hidden by policy. + * + * @param types internal inset types (WindowInsets.Type.InsetType) to hide + * @param fromIme true if this request originated from IME (InputMethodService). + */ + void hideInsets(int types, boolean fromIme); + void moved(int newX, int newY); void dispatchAppVisibility(boolean visible); void dispatchGetNewSurface(); diff --git a/core/java/android/view/InputMonitor.java b/core/java/android/view/InputMonitor.java index 1a1d7e682f6e..ad1f201ba3c1 100644 --- a/core/java/android/view/InputMonitor.java +++ b/core/java/android/view/InputMonitor.java @@ -40,8 +40,6 @@ public final class InputMonitor implements Parcelable { private static final boolean DEBUG = false; @NonNull - private final String mName; - @NonNull private final InputChannel mInputChannel; @NonNull private final IInputMonitorHost mHost; @@ -81,23 +79,19 @@ public final class InputMonitor implements Parcelable { - // Code below generated by codegen v1.0.1. + // Code below generated by codegen v1.0.7. // // DO NOT MODIFY! + // CHECKSTYLE:OFF Generated code // // To regenerate run: // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/view/InputMonitor.java - // - // CHECKSTYLE:OFF Generated code + @DataClass.Generated.Member public InputMonitor( - @NonNull String name, @NonNull InputChannel inputChannel, @NonNull IInputMonitorHost host) { - this.mName = name; - com.android.internal.util.AnnotationValidations.validate( - NonNull.class, null, mName); this.mInputChannel = inputChannel; com.android.internal.util.AnnotationValidations.validate( NonNull.class, null, mInputChannel); @@ -109,11 +103,6 @@ public final class InputMonitor implements Parcelable { } @DataClass.Generated.Member - public @NonNull String getName() { - return mName; - } - - @DataClass.Generated.Member public @NonNull InputChannel getInputChannel() { return mInputChannel; } @@ -130,7 +119,6 @@ public final class InputMonitor implements Parcelable { // String fieldNameToString() { ... } return "InputMonitor { " + - "name = " + mName + ", " + "inputChannel = " + mInputChannel + ", " + "host = " + mHost + " }"; @@ -142,7 +130,6 @@ public final class InputMonitor implements Parcelable { // You can override field parcelling by defining methods like: // void parcelFieldName(Parcel dest, int flags) { ... } - dest.writeString(mName); dest.writeTypedObject(mInputChannel, flags); dest.writeStrongInterface(mHost); } @@ -151,6 +138,26 @@ public final class InputMonitor implements Parcelable { @DataClass.Generated.Member public int describeContents() { return 0; } + /** @hide */ + @SuppressWarnings({"unchecked", "RedundantCast"}) + @DataClass.Generated.Member + /* package-private */ InputMonitor(Parcel in) { + // You can override field unparcelling by defining methods like: + // static FieldType unparcelFieldName(Parcel in) { ... } + + InputChannel inputChannel = (InputChannel) in.readTypedObject(InputChannel.CREATOR); + IInputMonitorHost host = IInputMonitorHost.Stub.asInterface(in.readStrongBinder()); + + this.mInputChannel = inputChannel; + com.android.internal.util.AnnotationValidations.validate( + NonNull.class, null, mInputChannel); + this.mHost = host; + com.android.internal.util.AnnotationValidations.validate( + NonNull.class, null, mHost); + + // onConstructed(); // You can define this method to get a callback + } + @DataClass.Generated.Member public static final @NonNull Parcelable.Creator<InputMonitor> CREATOR = new Parcelable.Creator<InputMonitor>() { @@ -160,26 +167,16 @@ public final class InputMonitor implements Parcelable { } @Override - @SuppressWarnings({"unchecked", "RedundantCast"}) public InputMonitor createFromParcel(Parcel in) { - // You can override field unparcelling by defining methods like: - // static FieldType unparcelFieldName(Parcel in) { ... } - - String name = in.readString(); - InputChannel inputChannel = (InputChannel) in.readTypedObject(InputChannel.CREATOR); - IInputMonitorHost host = IInputMonitorHost.Stub.asInterface(in.readStrongBinder()); - return new InputMonitor( - name, - inputChannel, - host); + return new InputMonitor(in); } }; @DataClass.Generated( - time = 1569871940995L, - codegenVersion = "1.0.1", + time = 1571177265149L, + codegenVersion = "1.0.7", sourceFile = "frameworks/base/core/java/android/view/InputMonitor.java", - inputSignatures = "private static final java.lang.String TAG\nprivate static final boolean DEBUG\nprivate final @android.annotation.NonNull java.lang.String mName\nprivate final @android.annotation.NonNull android.view.InputChannel mInputChannel\nprivate final @android.annotation.NonNull android.view.IInputMonitorHost mHost\npublic void pilferPointers()\npublic void dispose()\nclass InputMonitor extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genToString=true)") + inputSignatures = "private static final java.lang.String TAG\nprivate static final boolean DEBUG\nprivate final @android.annotation.NonNull android.view.InputChannel mInputChannel\nprivate final @android.annotation.NonNull android.view.IInputMonitorHost mHost\npublic void pilferPointers()\npublic void dispose()\nclass InputMonitor extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genToString=true)") @Deprecated private void __metadata() {} diff --git a/core/java/android/view/InsetsAnimationControlImpl.java b/core/java/android/view/InsetsAnimationControlImpl.java index 341c2147c64a..e4deffadc966 100644 --- a/core/java/android/view/InsetsAnimationControlImpl.java +++ b/core/java/android/view/InsetsAnimationControlImpl.java @@ -229,6 +229,10 @@ public class InsetsAnimationControlImpl implements WindowInsetsAnimationControll final InsetsSourceConsumer consumer = items.valueAt(i); final InsetsSource source = mInitialInsetsState.getSource(consumer.getType()); final InsetsSourceControl control = consumer.getControl(); + if (control == null) { + // Control may not be available for consumer yet or revoked. + continue; + } final SurfaceControl leash = consumer.getControl().getLeash(); mTmpMatrix.setTranslate(control.getSurfacePosition().x, control.getSurfacePosition().y); diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java index 5a8636d85a08..5bb4f63d62c8 100644 --- a/core/java/android/view/InsetsController.java +++ b/core/java/android/view/InsetsController.java @@ -251,6 +251,10 @@ public class InsetsController implements WindowInsetsController { @Override public void hide(@InsetType int types) { + hide(types, false /* fromIme */); + } + + void hide(@InsetType int types, boolean fromIme) { int typesReady = 0; final ArraySet<Integer> internalTypes = InsetsState.toInternalType(types); for (int i = internalTypes.size() - 1; i >= 0; i--) { @@ -265,7 +269,7 @@ public class InsetsController implements WindowInsetsController { } typesReady |= InsetsState.toPublicType(consumer.getType()); } - applyAnimation(typesReady, false /* show */, false /* fromIme */); + applyAnimation(typesReady, false /* show */, fromIme /* fromIme */); } @Override @@ -331,42 +335,35 @@ public class InsetsController implements WindowInsetsController { boolean isReady = true; for (int i = internalTypes.size() - 1; i >= 0; i--) { InsetsSourceConsumer consumer = getSourceConsumer(internalTypes.valueAt(i)); - // Double check for IME that IME target window has focus. - if (consumer.getType() != TYPE_IME || consumer.hasWindowFocus()) { - boolean setVisible = !consumer.isVisible(); - if (setVisible) { - // Show request - switch(consumer.requestShow(fromIme)) { - case ShowResult.SHOW_IMMEDIATELY: - typesReady |= InsetsState.toPublicType(consumer.getType()); - break; - case ShowResult.SHOW_DELAYED: - isReady = false; - break; - case ShowResult.SHOW_FAILED: - // IME cannot be shown (since it didn't have focus), proceed - // with animation of other types. - if (mPendingTypesToShow != 0) { - // remove IME from pending because view no longer has focus. - mPendingTypesToShow &= ~InsetsState.toPublicType(TYPE_IME); - } - break; - } - } else { - // Hide request - // TODO: Move notifyHidden() to beginning of the hide animation - // (when visibility actually changes using hideDirectly()). - consumer.notifyHidden(); - typesReady |= InsetsState.toPublicType(consumer.getType()); + boolean setVisible = !consumer.isVisible(); + if (setVisible) { + // Show request + switch(consumer.requestShow(fromIme)) { + case ShowResult.SHOW_IMMEDIATELY: + typesReady |= InsetsState.toPublicType(consumer.getType()); + break; + case ShowResult.SHOW_DELAYED: + isReady = false; + break; + case ShowResult.SHOW_FAILED: + // IME cannot be shown (since it didn't have focus), proceed + // with animation of other types. + if (mPendingTypesToShow != 0) { + // remove IME from pending because view no longer has focus. + mPendingTypesToShow &= ~InsetsState.toPublicType(TYPE_IME); + } + break; } - consumers.put(consumer.getType(), consumer); } else { - // window doesnt have focus, no-op. - isReady = false; - // TODO: Let the calling app know that window has lost focus and - // show()/hide()/controlWindowInsetsAnimation requests will be ignored. - typesReady &= ~InsetsState.toPublicType(consumer.getType()); + // Hide request + // TODO: Move notifyHidden() to beginning of the hide animation + // (when visibility actually changes using hideDirectly()). + if (!fromIme) { + consumer.notifyHidden(); + } + typesReady |= InsetsState.toPublicType(consumer.getType()); } + consumers.put(consumer.getType(), consumer); } return new Pair<>(typesReady, isReady); } diff --git a/core/java/android/view/ViewDebug.java b/core/java/android/view/ViewDebug.java index 8a1fd62c0201..ad59ae5d2bee 100644 --- a/core/java/android/view/ViewDebug.java +++ b/core/java/android/view/ViewDebug.java @@ -32,6 +32,7 @@ import android.graphics.RenderNode; import android.os.Debug; import android.os.Handler; import android.os.Looper; +import android.os.Message; import android.os.RemoteException; import android.util.DisplayMetrics; import android.util.Log; @@ -46,6 +47,7 @@ import java.io.DataOutputStream; import java.io.IOException; import java.io.OutputStream; import java.io.OutputStreamWriter; +import java.lang.annotation.Annotation; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -53,12 +55,12 @@ import java.lang.annotation.Target; import java.lang.reflect.AccessibleObject; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Member; import java.lang.reflect.Method; import java.util.ArrayDeque; -import java.util.ArrayList; +import java.util.Arrays; import java.util.HashMap; import java.util.concurrent.Callable; -import java.util.concurrent.CancellationException; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutionException; import java.util.concurrent.Executor; @@ -68,6 +70,7 @@ import java.util.concurrent.TimeoutException; import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.locks.ReentrantLock; import java.util.function.Function; +import java.util.stream.Stream; /** * Various debugging/tracing tools related to {@link View} and the view hierarchy. @@ -331,11 +334,83 @@ public class ViewDebug { public View findHierarchyView(String className, int hashCode); } - private static HashMap<Class<?>, Method[]> mCapturedViewMethodsForClasses = null; - private static HashMap<Class<?>, Field[]> mCapturedViewFieldsForClasses = null; + private abstract static class PropertyInfo<T extends Annotation, + R extends AccessibleObject & Member> { + + public final R member; + public final T property; + public final String name; + public final Class<?> returnType; + + public String entrySuffix = ""; + public String valueSuffix = ""; + + PropertyInfo(Class<T> property, R member, Class<?> returnType) { + this.member = member; + this.name = member.getName(); + this.property = member.getAnnotation(property); + this.returnType = returnType; + } + + public abstract Object invoke(Object target) throws Exception; + + static <T extends Annotation> PropertyInfo<T, ?> forMethod(Method method, + Class<T> property) { + // Ensure the method return and parameter types can be resolved. + try { + if ((method.getReturnType() == Void.class) + || (method.getParameterTypes().length != 0)) { + return null; + } + } catch (NoClassDefFoundError e) { + return null; + } + if (!method.isAnnotationPresent(property)) { + return null; + } + method.setAccessible(true); + + PropertyInfo info = new MethodPI(method, property); + info.entrySuffix = "()"; + info.valueSuffix = ";"; + return info; + } + + static <T extends Annotation> PropertyInfo<T, ?> forField(Field field, Class<T> property) { + if (!field.isAnnotationPresent(property)) { + return null; + } + field.setAccessible(true); + return new FieldPI<>(field, property); + } + } + + private static class MethodPI<T extends Annotation> extends PropertyInfo<T, Method> { + + MethodPI(Method method, Class<T> property) { + super(property, method, method.getReturnType()); + } + + @Override + public Object invoke(Object target) throws Exception { + return member.invoke(target); + } + } + + private static class FieldPI<T extends Annotation> extends PropertyInfo<T, Field> { + + FieldPI(Field field, Class<T> property) { + super(property, field, field.getType()); + } + + @Override + public Object invoke(Object target) throws Exception { + return member.get(target); + } + } // Maximum delay in ms after which we stop trying to capture a View's drawing - private static final int CAPTURE_TIMEOUT = 4000; + private static final int CAPTURE_TIMEOUT = 6000; private static final String REMOTE_COMMAND_CAPTURE = "CAPTURE"; private static final String REMOTE_COMMAND_DUMP = "DUMP"; @@ -346,9 +421,9 @@ public class ViewDebug { private static final String REMOTE_COMMAND_CAPTURE_LAYERS = "CAPTURE_LAYERS"; private static final String REMOTE_COMMAND_OUTPUT_DISPLAYLIST = "OUTPUT_DISPLAYLIST"; - private static HashMap<Class<?>, Field[]> sFieldsForClasses; - private static HashMap<Class<?>, Method[]> sMethodsForClasses; - private static HashMap<AccessibleObject, ExportedProperty> sAnnotations; + private static HashMap<Class<?>, PropertyInfo<ExportedProperty, ?>[]> sExportProperties; + private static HashMap<Class<?>, PropertyInfo<CapturedViewProperty, ?>[]> + sCapturedViewProperties; /** * @deprecated This enum is now unused @@ -1157,6 +1232,69 @@ public class ViewDebug { private static void dumpViewHierarchy(Context context, ViewGroup group, BufferedWriter out, int level, boolean skipChildren, boolean includeProperties) { + cacheExportedProperties(group.getClass()); + if (!skipChildren) { + cacheExportedPropertiesForChildren(group); + } + // Try to use the handler provided by the view + Handler handler = group.getHandler(); + // Fall back on using the main thread + if (handler == null) { + handler = new Handler(Looper.getMainLooper()); + } + + if (handler.getLooper() == Looper.myLooper()) { + dumpViewHierarchyOnUIThread(context, group, out, level, skipChildren, + includeProperties); + } else { + FutureTask task = new FutureTask(() -> + dumpViewHierarchyOnUIThread(context, group, out, level, skipChildren, + includeProperties), null); + Message msg = Message.obtain(handler, task); + msg.setAsynchronous(true); + handler.sendMessage(msg); + while (true) { + try { + task.get(CAPTURE_TIMEOUT, java.util.concurrent.TimeUnit.MILLISECONDS); + return; + } catch (InterruptedException e) { + // try again + } catch (ExecutionException | TimeoutException e) { + // Something unexpected happened. + throw new RuntimeException(e); + } + } + } + } + + private static void cacheExportedPropertiesForChildren(ViewGroup group) { + final int count = group.getChildCount(); + for (int i = 0; i < count; i++) { + final View view = group.getChildAt(i); + cacheExportedProperties(view.getClass()); + if (view instanceof ViewGroup) { + cacheExportedPropertiesForChildren((ViewGroup) view); + } + } + } + + private static void cacheExportedProperties(Class<?> klass) { + if (sExportProperties != null && sExportProperties.containsKey(klass)) { + return; + } + do { + for (PropertyInfo<ExportedProperty, ?> info : getExportedProperties(klass)) { + if (!info.returnType.isPrimitive() && info.property.deepExport()) { + cacheExportedProperties(info.returnType); + } + } + klass = klass.getSuperclass(); + } while (klass != Object.class); + } + + + private static void dumpViewHierarchyOnUIThread(Context context, ViewGroup group, + BufferedWriter out, int level, boolean skipChildren, boolean includeProperties) { if (!dumpView(context, group, out, level, includeProperties)) { return; } @@ -1169,16 +1307,16 @@ public class ViewDebug { for (int i = 0; i < count; i++) { final View view = group.getChildAt(i); if (view instanceof ViewGroup) { - dumpViewHierarchy(context, (ViewGroup) view, out, level + 1, skipChildren, - includeProperties); + dumpViewHierarchyOnUIThread(context, (ViewGroup) view, out, level + 1, + skipChildren, includeProperties); } else { dumpView(context, view, out, level + 1, includeProperties); } if (view.mOverlay != null) { ViewOverlay overlay = view.getOverlay(); ViewGroup overlayContainer = overlay.mOverlayViewGroup; - dumpViewHierarchy(context, overlayContainer, out, level + 2, skipChildren, - includeProperties); + dumpViewHierarchyOnUIThread(context, overlayContainer, out, level + 2, + skipChildren, includeProperties); } } if (group instanceof HierarchyHandler) { @@ -1212,81 +1350,28 @@ public class ViewDebug { return true; } - private static Field[] getExportedPropertyFields(Class<?> klass) { - if (sFieldsForClasses == null) { - sFieldsForClasses = new HashMap<Class<?>, Field[]>(); - } - if (sAnnotations == null) { - sAnnotations = new HashMap<AccessibleObject, ExportedProperty>(512); - } - - final HashMap<Class<?>, Field[]> map = sFieldsForClasses; - - Field[] fields = map.get(klass); - if (fields != null) { - return fields; - } - - try { - final Field[] declaredFields = klass.getDeclaredFieldsUnchecked(false); - final ArrayList<Field> foundFields = new ArrayList<Field>(); - for (final Field field : declaredFields) { - // Fields which can't be resolved have a null type. - if (field.getType() != null && field.isAnnotationPresent(ExportedProperty.class)) { - field.setAccessible(true); - foundFields.add(field); - sAnnotations.put(field, field.getAnnotation(ExportedProperty.class)); - } - } - fields = foundFields.toArray(new Field[foundFields.size()]); - map.put(klass, fields); - } catch (NoClassDefFoundError e) { - throw new AssertionError(e); - } - - return fields; + private static <T extends Annotation> PropertyInfo<T, ?>[] convertToPropertyInfos( + Method[] methods, Field[] fields, Class<T> property) { + return Stream.of(Arrays.stream(methods).map(m -> PropertyInfo.forMethod(m, property)), + Arrays.stream(fields).map(f -> PropertyInfo.forField(f, property))) + .flatMap(Function.identity()) + .filter(i -> i != null) + .toArray(PropertyInfo[]::new); } - private static Method[] getExportedPropertyMethods(Class<?> klass) { - if (sMethodsForClasses == null) { - sMethodsForClasses = new HashMap<Class<?>, Method[]>(100); - } - if (sAnnotations == null) { - sAnnotations = new HashMap<AccessibleObject, ExportedProperty>(512); - } - - final HashMap<Class<?>, Method[]> map = sMethodsForClasses; - - Method[] methods = map.get(klass); - if (methods != null) { - return methods; + private static PropertyInfo<ExportedProperty, ?>[] getExportedProperties(Class<?> klass) { + if (sExportProperties == null) { + sExportProperties = new HashMap<>(); } + final HashMap<Class<?>, PropertyInfo<ExportedProperty, ?>[]> map = sExportProperties; + PropertyInfo<ExportedProperty, ?>[] properties = sExportProperties.get(klass); - methods = klass.getDeclaredMethodsUnchecked(false); - - final ArrayList<Method> foundMethods = new ArrayList<Method>(); - for (final Method method : methods) { - // Ensure the method return and parameter types can be resolved. - try { - method.getReturnType(); - method.getParameterTypes(); - } catch (NoClassDefFoundError e) { - continue; - } - - if (method.getParameterTypes().length == 0 && - method.isAnnotationPresent(ExportedProperty.class) && - method.getReturnType() != Void.class) { - method.setAccessible(true); - foundMethods.add(method); - sAnnotations.put(method, method.getAnnotation(ExportedProperty.class)); - } + if (properties == null) { + properties = convertToPropertyInfos(klass.getDeclaredMethodsUnchecked(false), + klass.getDeclaredFieldsUnchecked(false), ExportedProperty.class); + map.put(klass, properties); } - - methods = foundMethods.toArray(new Method[foundMethods.size()]); - map.put(klass, methods); - - return methods; + return properties; } private static void dumpViewProperties(Context context, Object view, @@ -1305,233 +1390,97 @@ public class ViewDebug { Class<?> klass = view.getClass(); do { - exportFields(context, view, out, klass, prefix); - exportMethods(context, view, out, klass, prefix); + writeExportedProperties(context, view, out, klass, prefix); klass = klass.getSuperclass(); } while (klass != Object.class); } - private static Object callMethodOnAppropriateTheadBlocking(final Method method, - final Object object) throws IllegalAccessException, InvocationTargetException, - TimeoutException { - if (!(object instanceof View)) { - return method.invoke(object, (Object[]) null); - } - - final View view = (View) object; - Callable<Object> callable = new Callable<Object>() { - @Override - public Object call() throws IllegalAccessException, InvocationTargetException { - return method.invoke(view, (Object[]) null); - } - }; - FutureTask<Object> future = new FutureTask<Object>(callable); - // Try to use the handler provided by the view - Handler handler = view.getHandler(); - // Fall back on using the main thread - if (handler == null) { - handler = new Handler(android.os.Looper.getMainLooper()); - } - handler.post(future); - while (true) { - try { - return future.get(CAPTURE_TIMEOUT, java.util.concurrent.TimeUnit.MILLISECONDS); - } catch (ExecutionException e) { - Throwable t = e.getCause(); - if (t instanceof IllegalAccessException) { - throw (IllegalAccessException)t; - } - if (t instanceof InvocationTargetException) { - throw (InvocationTargetException)t; - } - throw new RuntimeException("Unexpected exception", t); - } catch (InterruptedException e) { - // Call get again - } catch (CancellationException e) { - throw new RuntimeException("Unexpected cancellation exception", e); - } - } - } - private static String formatIntToHexString(int value) { return "0x" + Integer.toHexString(value).toUpperCase(); } - private static void exportMethods(Context context, Object view, BufferedWriter out, + private static void writeExportedProperties(Context context, Object view, BufferedWriter out, Class<?> klass, String prefix) throws IOException { - - final Method[] methods = getExportedPropertyMethods(klass); - int count = methods.length; - for (int i = 0; i < count; i++) { - final Method method = methods[i]; + for (PropertyInfo<ExportedProperty, ?> info : getExportedProperties(klass)) { //noinspection EmptyCatchBlock + Object value; try { - Object methodValue = callMethodOnAppropriateTheadBlocking(method, view); - final Class<?> returnType = method.getReturnType(); - final ExportedProperty property = sAnnotations.get(method); - String categoryPrefix = - property.category().length() != 0 ? property.category() + ":" : ""; - - if (returnType == int.class) { - if (property.resolveId() && context != null) { - final int id = (Integer) methodValue; - methodValue = resolveId(context, id); - } else { - final FlagToString[] flagsMapping = property.flagMapping(); - if (flagsMapping.length > 0) { - final int intValue = (Integer) methodValue; - final String valuePrefix = - categoryPrefix + prefix + method.getName() + '_'; - exportUnrolledFlags(out, flagsMapping, intValue, valuePrefix); - } - - final IntToString[] mapping = property.mapping(); - if (mapping.length > 0) { - final int intValue = (Integer) methodValue; - boolean mapped = false; - int mappingCount = mapping.length; - for (int j = 0; j < mappingCount; j++) { - final IntToString mapper = mapping[j]; - if (mapper.from() == intValue) { - methodValue = mapper.to(); - mapped = true; - break; - } - } - - if (!mapped) { - methodValue = intValue; - } - } - } - } else if (returnType == int[].class) { - final int[] array = (int[]) methodValue; - final String valuePrefix = categoryPrefix + prefix + method.getName() + '_'; - final String suffix = "()"; + value = info.invoke(view); + } catch (Exception e) { + // ignore + continue; + } - exportUnrolledArray(context, out, property, array, valuePrefix, suffix); + String categoryPrefix = + info.property.category().length() != 0 ? info.property.category() + ":" : ""; - continue; - } else if (returnType == String[].class) { - final String[] array = (String[]) methodValue; - if (property.hasAdjacentMapping() && array != null) { - for (int j = 0; j < array.length; j += 2) { - if (array[j] != null) { - writeEntry(out, categoryPrefix + prefix, array[j], "()", - array[j + 1] == null ? "null" : array[j + 1]); - } + if (info.returnType == int.class || info.returnType == byte.class) { + if (info.property.resolveId() && context != null) { + final int id = (Integer) value; + value = resolveId(context, id); - } + } else if (info.property.formatToHexString()) { + if (info.returnType == int.class) { + value = formatIntToHexString((Integer) value); + } else if (info.returnType == byte.class) { + value = "0x" + + HexEncoding.encodeToString((Byte) value, true); } - - continue; - } else if (!returnType.isPrimitive()) { - if (property.deepExport()) { - dumpViewProperties(context, methodValue, out, prefix + property.prefix()); - continue; + } else { + final ViewDebug.FlagToString[] flagsMapping = info.property.flagMapping(); + if (flagsMapping.length > 0) { + final int intValue = (Integer) value; + final String valuePrefix = + categoryPrefix + prefix + info.name + '_'; + exportUnrolledFlags(out, flagsMapping, intValue, valuePrefix); } - } - - writeEntry(out, categoryPrefix + prefix, method.getName(), "()", methodValue); - } catch (IllegalAccessException e) { - } catch (InvocationTargetException e) { - } catch (TimeoutException e) { - } - } - } - - private static void exportFields(Context context, Object view, BufferedWriter out, - Class<?> klass, String prefix) throws IOException { - - final Field[] fields = getExportedPropertyFields(klass); - int count = fields.length; - for (int i = 0; i < count; i++) { - final Field field = fields[i]; - - //noinspection EmptyCatchBlock - try { - Object fieldValue = null; - final Class<?> type = field.getType(); - final ExportedProperty property = sAnnotations.get(field); - String categoryPrefix = - property.category().length() != 0 ? property.category() + ":" : ""; - - if (type == int.class || type == byte.class) { - if (property.resolveId() && context != null) { - final int id = field.getInt(view); - fieldValue = resolveId(context, id); - } else { - final FlagToString[] flagsMapping = property.flagMapping(); - if (flagsMapping.length > 0) { - final int intValue = field.getInt(view); - final String valuePrefix = - categoryPrefix + prefix + field.getName() + '_'; - exportUnrolledFlags(out, flagsMapping, intValue, valuePrefix); - } - - final IntToString[] mapping = property.mapping(); - if (mapping.length > 0) { - final int intValue = field.getInt(view); - int mappingCount = mapping.length; - for (int j = 0; j < mappingCount; j++) { - final IntToString mapped = mapping[j]; - if (mapped.from() == intValue) { - fieldValue = mapped.to(); - break; - } - } - - if (fieldValue == null) { - fieldValue = intValue; + final ViewDebug.IntToString[] mapping = info.property.mapping(); + if (mapping.length > 0) { + final int intValue = (Integer) value; + boolean mapped = false; + int mappingCount = mapping.length; + for (int j = 0; j < mappingCount; j++) { + final ViewDebug.IntToString mapper = mapping[j]; + if (mapper.from() == intValue) { + value = mapper.to(); + mapped = true; + break; } } - if (property.formatToHexString()) { - fieldValue = field.get(view); - if (type == int.class) { - fieldValue = formatIntToHexString((Integer) fieldValue); - } else if (type == byte.class) { - fieldValue = "0x" - + HexEncoding.encodeToString((Byte) fieldValue, true); - } + if (!mapped) { + value = intValue; } } - } else if (type == int[].class) { - final int[] array = (int[]) field.get(view); - final String valuePrefix = categoryPrefix + prefix + field.getName() + '_'; - final String suffix = ""; - - exportUnrolledArray(context, out, property, array, valuePrefix, suffix); + } + } else if (info.returnType == int[].class) { + final int[] array = (int[]) value; + final String valuePrefix = categoryPrefix + prefix + info.name + '_'; + exportUnrolledArray(context, out, info.property, array, valuePrefix, + info.entrySuffix); - continue; - } else if (type == String[].class) { - final String[] array = (String[]) field.get(view); - if (property.hasAdjacentMapping() && array != null) { - for (int j = 0; j < array.length; j += 2) { - if (array[j] != null) { - writeEntry(out, categoryPrefix + prefix, array[j], "", - array[j + 1] == null ? "null" : array[j + 1]); - } + continue; + } else if (info.returnType == String[].class) { + final String[] array = (String[]) value; + if (info.property.hasAdjacentMapping() && array != null) { + for (int j = 0; j < array.length; j += 2) { + if (array[j] != null) { + writeEntry(out, categoryPrefix + prefix, array[j], + info.entrySuffix, array[j + 1] == null ? "null" : array[j + 1]); } } - - continue; - } else if (!type.isPrimitive()) { - if (property.deepExport()) { - dumpViewProperties(context, field.get(view), out, prefix + - property.prefix()); - continue; - } } - if (fieldValue == null) { - fieldValue = field.get(view); + continue; + } else if (!info.returnType.isPrimitive()) { + if (info.property.deepExport()) { + dumpViewProperties(context, value, out, prefix + info.property.prefix()); + continue; } - - writeEntry(out, categoryPrefix + prefix, field.getName(), "", fieldValue); - } catch (IllegalAccessException e) { } + + writeEntry(out, categoryPrefix + prefix, info.name, info.entrySuffix, value); } } @@ -1721,91 +1670,40 @@ public class ViewDebug { } } - private static Field[] capturedViewGetPropertyFields(Class<?> klass) { - if (mCapturedViewFieldsForClasses == null) { - mCapturedViewFieldsForClasses = new HashMap<Class<?>, Field[]>(); - } - final HashMap<Class<?>, Field[]> map = mCapturedViewFieldsForClasses; - - Field[] fields = map.get(klass); - if (fields != null) { - return fields; - } - - final ArrayList<Field> foundFields = new ArrayList<Field>(); - fields = klass.getFields(); - - int count = fields.length; - for (int i = 0; i < count; i++) { - final Field field = fields[i]; - if (field.isAnnotationPresent(CapturedViewProperty.class)) { - field.setAccessible(true); - foundFields.add(field); - } - } - - fields = foundFields.toArray(new Field[foundFields.size()]); - map.put(klass, fields); - - return fields; - } - - private static Method[] capturedViewGetPropertyMethods(Class<?> klass) { - if (mCapturedViewMethodsForClasses == null) { - mCapturedViewMethodsForClasses = new HashMap<Class<?>, Method[]>(); - } - final HashMap<Class<?>, Method[]> map = mCapturedViewMethodsForClasses; - - Method[] methods = map.get(klass); - if (methods != null) { - return methods; + private static PropertyInfo<CapturedViewProperty, ?>[] getCapturedViewProperties( + Class<?> klass) { + if (sCapturedViewProperties == null) { + sCapturedViewProperties = new HashMap<>(); } + final HashMap<Class<?>, PropertyInfo<CapturedViewProperty, ?>[]> map = + sCapturedViewProperties; - final ArrayList<Method> foundMethods = new ArrayList<Method>(); - methods = klass.getMethods(); - - int count = methods.length; - for (int i = 0; i < count; i++) { - final Method method = methods[i]; - if (method.getParameterTypes().length == 0 && - method.isAnnotationPresent(CapturedViewProperty.class) && - method.getReturnType() != Void.class) { - method.setAccessible(true); - foundMethods.add(method); - } + PropertyInfo<CapturedViewProperty, ?>[] infos = map.get(klass); + if (infos == null) { + infos = convertToPropertyInfos(klass.getMethods(), klass.getFields(), + CapturedViewProperty.class); + map.put(klass, infos); } - - methods = foundMethods.toArray(new Method[foundMethods.size()]); - map.put(klass, methods); - - return methods; + return infos; } - private static String capturedViewExportMethods(Object obj, Class<?> klass, - String prefix) { - + private static String exportCapturedViewProperties(Object obj, Class<?> klass, String prefix) { if (obj == null) { return "null"; } StringBuilder sb = new StringBuilder(); - final Method[] methods = capturedViewGetPropertyMethods(klass); - int count = methods.length; - for (int i = 0; i < count; i++) { - final Method method = methods[i]; + for (PropertyInfo<CapturedViewProperty, ?> pi : getCapturedViewProperties(klass)) { try { - Object methodValue = method.invoke(obj, (Object[]) null); - final Class<?> returnType = method.getReturnType(); + Object methodValue = pi.invoke(obj); - CapturedViewProperty property = method.getAnnotation(CapturedViewProperty.class); - if (property.retrieveReturn()) { + if (pi.property.retrieveReturn()) { //we are interested in the second level data only - sb.append(capturedViewExportMethods(methodValue, returnType, method.getName() + "#")); + sb.append(exportCapturedViewProperties(methodValue, pi.returnType, + pi.name + "#")); } else { - sb.append(prefix); - sb.append(method.getName()); - sb.append("()="); + sb.append(prefix).append(pi.name).append(pi.entrySuffix).append("="); if (methodValue != null) { final String value = methodValue.toString().replace("\n", "\\n"); @@ -1813,47 +1711,10 @@ public class ViewDebug { } else { sb.append("null"); } - sb.append("; "); - } - } catch (IllegalAccessException e) { - //Exception IllegalAccess, it is OK here - //we simply ignore this method - } catch (InvocationTargetException e) { - //Exception InvocationTarget, it is OK here - //we simply ignore this method - } - } - return sb.toString(); - } - - private static String capturedViewExportFields(Object obj, Class<?> klass, String prefix) { - if (obj == null) { - return "null"; - } - - StringBuilder sb = new StringBuilder(); - final Field[] fields = capturedViewGetPropertyFields(klass); - - int count = fields.length; - for (int i = 0; i < count; i++) { - final Field field = fields[i]; - try { - Object fieldValue = field.get(obj); - - sb.append(prefix); - sb.append(field.getName()); - sb.append("="); - - if (fieldValue != null) { - final String value = fieldValue.toString().replace("\n", "\\n"); - sb.append(value); - } else { - sb.append("null"); + sb.append(pi.valueSuffix).append(" "); } - sb.append(' '); - } catch (IllegalAccessException e) { - //Exception IllegalAccess, it is OK here - //we simply ignore this field + } catch (Exception e) { + //It is OK here, we simply ignore this property } } return sb.toString(); @@ -1869,8 +1730,7 @@ public class ViewDebug { public static void dumpCapturedView(String tag, Object view) { Class<?> klass = view.getClass(); StringBuilder sb = new StringBuilder(klass.getName() + ": "); - sb.append(capturedViewExportFields(view, klass, "")); - sb.append(capturedViewExportMethods(view, klass, "")); + sb.append(exportCapturedViewProperties(view, klass, "")); Log.d(tag, sb.toString()); } diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 9ddd84f1943c..20dc23492ae5 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -4570,6 +4570,7 @@ public final class ViewRootImpl implements ViewParent, private static final int MSG_SYSTEM_GESTURE_EXCLUSION_CHANGED = 32; private static final int MSG_LOCATION_IN_PARENT_DISPLAY_CHANGED = 33; private static final int MSG_SHOW_INSETS = 34; + private static final int MSG_HIDE_INSETS = 35; final class ViewRootHandler extends Handler { @@ -4636,6 +4637,8 @@ public final class ViewRootImpl implements ViewParent, return "MSG_LOCATION_IN_PARENT_DISPLAY_CHANGED"; case MSG_SHOW_INSETS: return "MSG_SHOW_INSETS"; + case MSG_HIDE_INSETS: + return "MSG_HIDE_INSETS"; } return super.getMessageName(message); } @@ -4754,6 +4757,10 @@ public final class ViewRootImpl implements ViewParent, mInsetsController.show(msg.arg1, msg.arg2 == 1); break; } + case MSG_HIDE_INSETS: { + mInsetsController.hide(msg.arg1, msg.arg2 == 1); + break; + } case MSG_WINDOW_MOVED: if (mAdded) { final int w = mWinFrame.width(); @@ -7559,6 +7566,10 @@ public final class ViewRootImpl implements ViewParent, mHandler.obtainMessage(MSG_SHOW_INSETS, types, fromIme ? 1 : 0).sendToTarget(); } + private void hideInsets(@InsetType int types, boolean fromIme) { + mHandler.obtainMessage(MSG_HIDE_INSETS, types, fromIme ? 1 : 0).sendToTarget(); + } + public void dispatchMoved(int newX, int newY) { if (DEBUG_LAYOUT) Log.v(mTag, "Window moved " + this + ": newX=" + newX + " newY=" + newY); if (mTranslator != null) { @@ -8682,6 +8693,14 @@ public final class ViewRootImpl implements ViewParent, } @Override + public void hideInsets(@InsetType int types, boolean fromIme) { + final ViewRootImpl viewAncestor = mViewAncestor.get(); + if (viewAncestor != null) { + viewAncestor.hideInsets(types, fromIme); + } + } + + @Override public void moved(int newX, int newY) { final ViewRootImpl viewAncestor = mViewAncestor.get(); if (viewAncestor != null) { diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java index 0b42cd93eb17..db76bb6d3cde 100644 --- a/core/java/android/view/WindowManager.java +++ b/core/java/android/view/WindowManager.java @@ -1680,8 +1680,9 @@ public interface WindowManager extends ViewManager { * to determine its default behavior. * * {@hide} */ - @UnsupportedAppUsage - public static final int PRIVATE_FLAG_SHOW_FOR_ALL_USERS = 0x00000010; + @SystemApi + @RequiresPermission(permission.INTERNAL_SYSTEM_WINDOW) + public static final int SYSTEM_FLAG_SHOW_FOR_ALL_USERS = 0x00000010; /** * Never animate position changes of the window. @@ -1842,6 +1843,7 @@ public interface WindowManager extends ViewManager { @Retention(RetentionPolicy.SOURCE) @IntDef(flag = true, prefix = { "SYSTEM_FLAG_" }, value = { SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS, + SYSTEM_FLAG_SHOW_FOR_ALL_USERS, }) public @interface SystemFlags {} @@ -1863,8 +1865,8 @@ public interface WindowManager extends ViewManager { equals = PRIVATE_FLAG_WANTS_OFFSET_NOTIFICATIONS, name = "WANTS_OFFSET_NOTIFICATIONS"), @ViewDebug.FlagToString( - mask = PRIVATE_FLAG_SHOW_FOR_ALL_USERS, - equals = PRIVATE_FLAG_SHOW_FOR_ALL_USERS, + mask = SYSTEM_FLAG_SHOW_FOR_ALL_USERS, + equals = SYSTEM_FLAG_SHOW_FOR_ALL_USERS, name = "SHOW_FOR_ALL_USERS"), @ViewDebug.FlagToString( mask = PRIVATE_FLAG_NO_MOVE_ANIMATION, diff --git a/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java b/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java index 925a5894db06..0b15cd06a7ea 100644 --- a/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java +++ b/core/java/com/android/internal/accessibility/AccessibilityShortcutController.java @@ -246,7 +246,7 @@ public class AccessibilityShortcutController { Toast warningToast = mFrameworkObjectProvider.makeToastFromText( mContext, toastMessage, Toast.LENGTH_LONG); warningToast.getWindowParams().privateFlags |= - WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS; + WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS; warningToast.show(); } diff --git a/core/java/com/android/internal/app/procstats/AssociationState.java b/core/java/com/android/internal/app/procstats/AssociationState.java index 0ed252400aa1..1d4239f7c6ed 100644 --- a/core/java/com/android/internal/app/procstats/AssociationState.java +++ b/core/java/com/android/internal/app/procstats/AssociationState.java @@ -950,6 +950,14 @@ public final class AssociationState { proto.write(PackageAssociationProcessStatsProto.COMPONENT_NAME, mName); + proto.write(PackageAssociationProcessStatsProto.TOTAL_COUNT, mTotalCount); + proto.write(PackageAssociationProcessStatsProto.TOTAL_DURATION_MS, getTotalDuration(now)); + if (mTotalActiveCount != 0) { + proto.write(PackageAssociationProcessStatsProto.ACTIVE_COUNT, mTotalActiveCount); + proto.write(PackageAssociationProcessStatsProto.ACTIVE_DURATION_MS, + getActiveDuration(now)); + } + final int NSRC = mSources.size(); for (int isrc = 0; isrc < NSRC; isrc++) { final SourceKey key = mSources.keyAt(isrc); diff --git a/core/java/com/android/internal/content/FileSystemProvider.java b/core/java/com/android/internal/content/FileSystemProvider.java index cdb79abbb7ce..f5708a5c89af 100644 --- a/core/java/com/android/internal/content/FileSystemProvider.java +++ b/core/java/com/android/internal/content/FileSystemProvider.java @@ -17,6 +17,7 @@ package com.android.internal.content; import android.annotation.CallSuper; +import android.annotation.NonNull; import android.annotation.Nullable; import android.content.ContentResolver; import android.content.Intent; @@ -552,6 +553,11 @@ public abstract class FileSystemProvider extends DocumentsProvider { flags |= Document.FLAG_SUPPORTS_DELETE; flags |= Document.FLAG_SUPPORTS_RENAME; flags |= Document.FLAG_SUPPORTS_MOVE; + + if (shouldBlockFromTree(docId)) { + flags |= Document.FLAG_DIR_BLOCKS_TREE; + } + } else { flags |= Document.FLAG_SUPPORTS_WRITE; flags |= Document.FLAG_SUPPORTS_DELETE; @@ -592,6 +598,10 @@ public abstract class FileSystemProvider extends DocumentsProvider { return row; } + protected boolean shouldBlockFromTree(@NonNull String docId) { + return false; + } + protected boolean typeSupportsMetadata(String mimeType) { return MetadataReader.isSupportedMimeType(mimeType) || Document.MIME_TYPE_DIR.equals(mimeType); diff --git a/core/java/com/android/internal/statusbar/IStatusBarService.aidl b/core/java/com/android/internal/statusbar/IStatusBarService.aidl index a845b587c49f..659134adec78 100644 --- a/core/java/com/android/internal/statusbar/IStatusBarService.aidl +++ b/core/java/com/android/internal/statusbar/IStatusBarService.aidl @@ -17,6 +17,7 @@ package com.android.internal.statusbar; import android.app.Notification; +import android.net.Uri; import android.content.ComponentName; import android.graphics.Rect; import android.os.Bundle; @@ -77,6 +78,7 @@ interface IStatusBarService void onNotificationSettingsViewed(String key); void setSystemUiVisibility(int displayId, int vis, int mask, String cause); void onNotificationBubbleChanged(String key, boolean isBubble); + void grantInlineReplyUriPermission(String key, in Uri uri); void onGlobalActionsShown(); void onGlobalActionsHidden(); diff --git a/core/java/com/android/internal/view/BaseIWindow.java b/core/java/com/android/internal/view/BaseIWindow.java index 0e078dd3732d..7e1f13afc2cb 100644 --- a/core/java/com/android/internal/view/BaseIWindow.java +++ b/core/java/com/android/internal/view/BaseIWindow.java @@ -80,6 +80,10 @@ public class BaseIWindow extends IWindow.Stub { } @Override + public void hideInsets(@InsetType int types, boolean fromIme) throws RemoteException { + } + + @Override public void moved(int newX, int newY) { } diff --git a/core/java/com/android/internal/widget/LockPatternChecker.java b/core/java/com/android/internal/widget/LockPatternChecker.java index 09bc28c1f5ec..85a45fd8e0c0 100644 --- a/core/java/com/android/internal/widget/LockPatternChecker.java +++ b/core/java/com/android/internal/widget/LockPatternChecker.java @@ -1,13 +1,9 @@ package com.android.internal.widget; -import android.annotation.UnsupportedAppUsage; import android.os.AsyncTask; import com.android.internal.widget.LockPatternUtils.RequestThrottledException; -import java.util.ArrayList; -import java.util.List; - /** * Helper class to check/verify PIN/Password/Pattern asynchronously. */ @@ -53,34 +49,28 @@ public final class LockPatternChecker { } /** - * Verify a pattern asynchronously. + * Verify a lockscreen credential asynchronously. * * @param utils The LockPatternUtils instance to use. - * @param pattern The pattern to check. - * @param challenge The challenge to verify against the pattern. - * @param userId The user to check against the pattern. + * @param credential The credential to check. + * @param challenge The challenge to verify against the credential. + * @param userId The user to check against the credential. * @param callback The callback to be invoked with the verification result. */ - public static AsyncTask<?, ?, ?> verifyPattern(final LockPatternUtils utils, - final List<LockPatternView.Cell> pattern, + public static AsyncTask<?, ?, ?> verifyCredential(final LockPatternUtils utils, + final LockscreenCredential credential, final long challenge, final int userId, final OnVerifyCallback callback) { + // Create a copy of the credential since checking credential is asynchrounous. + final LockscreenCredential credentialCopy = credential.duplicate(); AsyncTask<Void, Void, byte[]> task = new AsyncTask<Void, Void, byte[]>() { private int mThrottleTimeout; - private List<LockPatternView.Cell> patternCopy; - - @Override - protected void onPreExecute() { - // Make a copy of the pattern to prevent race conditions. - // No need to clone the individual cells because they are immutable. - patternCopy = new ArrayList(pattern); - } @Override protected byte[] doInBackground(Void... args) { try { - return utils.verifyPattern(patternCopy, challenge, userId); + return utils.verifyCredential(credentialCopy, challenge, userId); } catch (RequestThrottledException ex) { mThrottleTimeout = ex.getTimeoutMs(); return null; @@ -90,6 +80,12 @@ public final class LockPatternChecker { @Override protected void onPostExecute(byte[] result) { callback.onVerified(result, mThrottleTimeout); + credentialCopy.zeroize(); + } + + @Override + protected void onCancelled() { + credentialCopy.zeroize(); } }; task.execute(); @@ -97,32 +93,26 @@ public final class LockPatternChecker { } /** - * Checks a pattern asynchronously. + * Checks a lockscreen credential asynchronously. * * @param utils The LockPatternUtils instance to use. - * @param pattern The pattern to check. - * @param userId The user to check against the pattern. + * @param credential The credential to check. + * @param userId The user to check against the credential. * @param callback The callback to be invoked with the check result. */ - public static AsyncTask<?, ?, ?> checkPattern(final LockPatternUtils utils, - final List<LockPatternView.Cell> pattern, + public static AsyncTask<?, ?, ?> checkCredential(final LockPatternUtils utils, + final LockscreenCredential credential, final int userId, final OnCheckCallback callback) { + // Create a copy of the credential since checking credential is asynchrounous. + final LockscreenCredential credentialCopy = credential.duplicate(); AsyncTask<Void, Void, Boolean> task = new AsyncTask<Void, Void, Boolean>() { private int mThrottleTimeout; - private List<LockPatternView.Cell> patternCopy; - - @Override - protected void onPreExecute() { - // Make a copy of the pattern to prevent race conditions. - // No need to clone the individual cells because they are immutable. - patternCopy = new ArrayList(pattern); - } @Override protected Boolean doInBackground(Void... args) { try { - return utils.checkPattern(patternCopy, userId, callback::onEarlyMatched); + return utils.checkCredential(credentialCopy, userId, callback::onEarlyMatched); } catch (RequestThrottledException ex) { mThrottleTimeout = ex.getTimeoutMs(); return false; @@ -132,11 +122,13 @@ public final class LockPatternChecker { @Override protected void onPostExecute(Boolean result) { callback.onChecked(result, mThrottleTimeout); + credentialCopy.zeroize(); } @Override protected void onCancelled() { callback.onCancelled(); + credentialCopy.zeroize(); } }; task.execute(); @@ -144,84 +136,29 @@ public final class LockPatternChecker { } /** - * Verify a password asynchronously. + * Perform a lockscreen credential verification explicitly on a managed profile with unified + * challenge, using the parent user's credential. * * @param utils The LockPatternUtils instance to use. - * @param password The password to check. - * @param challenge The challenge to verify against the pattern. - * @param userId The user to check against the pattern. - * @param callback The callback to be invoked with the verification result. - * - * @deprecated Pass the password as a byte array. - */ - @Deprecated - public static AsyncTask<?, ?, ?> verifyPassword(final LockPatternUtils utils, - final String password, - final long challenge, - final int userId, - final OnVerifyCallback callback) { - byte[] passwordBytes = password != null ? password.getBytes() : null; - return verifyPassword(utils, passwordBytes, challenge, userId, callback); - } - - /** - * Verify a password asynchronously. - * - * @param utils The LockPatternUtils instance to use. - * @param password The password to check. - * @param challenge The challenge to verify against the pattern. - * @param userId The user to check against the pattern. - * @param callback The callback to be invoked with the verification result. - */ - public static AsyncTask<?, ?, ?> verifyPassword(final LockPatternUtils utils, - final byte[] password, - final long challenge, - final int userId, - final OnVerifyCallback callback) { - AsyncTask<Void, Void, byte[]> task = new AsyncTask<Void, Void, byte[]>() { - private int mThrottleTimeout; - - @Override - protected byte[] doInBackground(Void... args) { - try { - return utils.verifyPassword(password, challenge, userId); - } catch (RequestThrottledException ex) { - mThrottleTimeout = ex.getTimeoutMs(); - return null; - } - } - - @Override - protected void onPostExecute(byte[] result) { - callback.onVerified(result, mThrottleTimeout); - } - }; - task.execute(); - return task; - } - - /** - * Verify a password asynchronously. - * - * @param utils The LockPatternUtils instance to use. - * @param password The password to check. - * @param challenge The challenge to verify against the pattern. - * @param userId The user to check against the pattern. + * @param credential The credential to check. + * @param challenge The challenge to verify against the credential. + * @param userId The user to check against the credential. * @param callback The callback to be invoked with the verification result. */ public static AsyncTask<?, ?, ?> verifyTiedProfileChallenge(final LockPatternUtils utils, - final byte[] password, - final boolean isPattern, + final LockscreenCredential credential, final long challenge, final int userId, final OnVerifyCallback callback) { + // Create a copy of the credential since checking credential is asynchrounous. + final LockscreenCredential credentialCopy = credential.duplicate(); AsyncTask<Void, Void, byte[]> task = new AsyncTask<Void, Void, byte[]>() { private int mThrottleTimeout; @Override protected byte[] doInBackground(Void... args) { try { - return utils.verifyTiedProfileChallenge(password, isPattern, challenge, userId); + return utils.verifyTiedProfileChallenge(credentialCopy, challenge, userId); } catch (RequestThrottledException ex) { mThrottleTimeout = ex.getTimeoutMs(); return null; @@ -231,64 +168,12 @@ public final class LockPatternChecker { @Override protected void onPostExecute(byte[] result) { callback.onVerified(result, mThrottleTimeout); - } - }; - task.execute(); - return task; - } - - /** - * Checks a password asynchronously. - * - * @param utils The LockPatternUtils instance to use. - * @param password The password to check. - * @param userId The user to check against the pattern. - * @param callback The callback to be invoked with the check result. - * @deprecated Pass passwords as byte[] - */ - @UnsupportedAppUsage - @Deprecated - public static AsyncTask<?, ?, ?> checkPassword(final LockPatternUtils utils, - final String password, - final int userId, - final OnCheckCallback callback) { - byte[] passwordBytes = password != null ? password.getBytes() : null; - return checkPassword(utils, passwordBytes, userId, callback); - } - - /** - * Checks a password asynchronously. - * - * @param utils The LockPatternUtils instance to use. - * @param passwordBytes The password to check. - * @param userId The user to check against the pattern. - * @param callback The callback to be invoked with the check result. - */ - public static AsyncTask<?, ?, ?> checkPassword(final LockPatternUtils utils, - final byte[] passwordBytes, - final int userId, - final OnCheckCallback callback) { - AsyncTask<Void, Void, Boolean> task = new AsyncTask<Void, Void, Boolean>() { - private int mThrottleTimeout; - - @Override - protected Boolean doInBackground(Void... args) { - try { - return utils.checkPassword(passwordBytes, userId, callback::onEarlyMatched); - } catch (RequestThrottledException ex) { - mThrottleTimeout = ex.getTimeoutMs(); - return false; - } - } - - @Override - protected void onPostExecute(Boolean result) { - callback.onChecked(result, mThrottleTimeout); + credentialCopy.zeroize(); } @Override protected void onCancelled() { - callback.onCancelled(); + credentialCopy.zeroize(); } }; task.execute(); diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java index 070121cd1feb..1daa25af11fc 100644 --- a/core/java/com/android/internal/widget/LockPatternUtils.java +++ b/core/java/com/android/internal/widget/LockPatternUtils.java @@ -26,10 +26,10 @@ import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_SOMETHING; import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED; import android.annotation.IntDef; +import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.UnsupportedAppUsage; import android.app.admin.DevicePolicyManager; -import android.app.admin.PasswordMetrics; import android.app.trust.IStrongAuthTracker; import android.app.trust.TrustManager; import android.content.ComponentName; @@ -365,11 +365,24 @@ public class LockPatternUtils { null /* componentName */, userId); } - private byte[] verifyCredential(byte[] credential, int type, long challenge, int userId) - throws RequestThrottledException { + /** + * Check to see if a credential matches the saved one. + * If credential matches, return an opaque attestation that the challenge was verified. + * + * @param credential The credential to check. + * @param challenge The challenge to verify against the credential + * @return the attestation that the challenge was verified, or null + * @param userId The user whose credential is being verified + * @throws RequestThrottledException if credential verification is being throttled due to + * to many incorrect attempts. + * @throws IllegalStateException if called on the main thread. + */ + public byte[] verifyCredential(@NonNull LockscreenCredential credential, long challenge, + int userId) throws RequestThrottledException { + throwIfCalledOnMainThread(); try { - VerifyCredentialResponse response = getLockSettings().verifyCredential(credential, - type, challenge, userId); + VerifyCredentialResponse response = getLockSettings().verifyCredential( + credential.getCredential(), credential.getType(), challenge, userId); if (response.getResponseCode() == VerifyCredentialResponse.RESPONSE_OK) { return response.getPayload(); } else if (response.getResponseCode() == VerifyCredentialResponse.RESPONSE_RETRY) { @@ -382,11 +395,24 @@ public class LockPatternUtils { } } - private boolean checkCredential(byte[] credential, int type, int userId, + /** + * Check to see if a credential matches the saved one. + * + * @param credential The credential to check. + * @param userId The user whose credential is being checked + * @param progressCallback callback to deliver early signal that the credential matches + * @return {@code true} if credential matches, {@code false} otherwise + * @throws RequestThrottledException if credential verification is being throttled due to + * to many incorrect attempts. + * @throws IllegalStateException if called on the main thread. + */ + public boolean checkCredential(@NonNull LockscreenCredential credential, int userId, @Nullable CheckCredentialProgressCallback progressCallback) throws RequestThrottledException { + throwIfCalledOnMainThread(); try { - VerifyCredentialResponse response = getLockSettings().checkCredential(credential, type, + VerifyCredentialResponse response = getLockSettings().checkCredential( + credential.getCredential(), credential.getType(), userId, wrapCallback(progressCallback)); if (response.getResponseCode() == VerifyCredentialResponse.RESPONSE_OK) { @@ -402,79 +428,26 @@ public class LockPatternUtils { } /** - * Check to see if a pattern matches the saved pattern. - * If pattern matches, return an opaque attestation that the challenge - * was verified. + * Check if the credential of a managed profile with unified challenge matches. In this context, + * The credential should be the parent user's lockscreen password. If credential matches, + * return an opaque attestation associated with the managed profile that the challenge was + * verified. * - * @param pattern The pattern to check. - * @param challenge The challenge to verify against the pattern - * @return the attestation that the challenge was verified, or null. - */ - public byte[] verifyPattern(List<LockPatternView.Cell> pattern, long challenge, int userId) - throws RequestThrottledException { - throwIfCalledOnMainThread(); - return verifyCredential(patternToByteArray(pattern), CREDENTIAL_TYPE_PATTERN, challenge, - userId); - } - - /** - * Check to see if a pattern matches the saved pattern. If no pattern exists, - * always returns true. - * @param pattern The pattern to check. - * @return Whether the pattern matches the stored one. - */ - public boolean checkPattern(List<LockPatternView.Cell> pattern, int userId) - throws RequestThrottledException { - return checkPattern(pattern, userId, null /* progressCallback */); - } - - /** - * Check to see if a pattern matches the saved pattern. If no pattern exists, - * always returns true. - * @param pattern The pattern to check. - * @return Whether the pattern matches the stored one. - */ - public boolean checkPattern(List<LockPatternView.Cell> pattern, int userId, - @Nullable CheckCredentialProgressCallback progressCallback) - throws RequestThrottledException { - throwIfCalledOnMainThread(); - return checkCredential(patternToByteArray(pattern), CREDENTIAL_TYPE_PATTERN, userId, - progressCallback); - } - - /** - * Check to see if a password matches the saved password. - * If password matches, return an opaque attestation that the challenge - * was verified. - * - * @param password The password to check. - * @param challenge The challenge to verify against the password - * @return the attestation that the challenge was verified, or null. - */ - public byte[] verifyPassword(byte[] password, long challenge, int userId) - throws RequestThrottledException { - throwIfCalledOnMainThread(); - return verifyCredential(password, CREDENTIAL_TYPE_PASSWORD, challenge, userId); - } - - - /** - * Check to see if a password matches the saved password. - * If password matches, return an opaque attestation that the challenge - * was verified. - * - * @param password The password to check. - * @param challenge The challenge to verify against the password - * @return the attestation that the challenge was verified, or null. - */ - public byte[] verifyTiedProfileChallenge(byte[] password, boolean isPattern, long challenge, - int userId) throws RequestThrottledException { + * @param credential The parent user's credential to check. + * @param challenge The challenge to verify against the credential + * @return the attestation that the challenge was verified, or null + * @param userId The managed profile user id + * @throws RequestThrottledException if credential verification is being throttled due to + * to many incorrect attempts. + * @throws IllegalStateException if called on the main thread. + */ + public byte[] verifyTiedProfileChallenge(@NonNull LockscreenCredential credential, + long challenge, int userId) throws RequestThrottledException { throwIfCalledOnMainThread(); try { VerifyCredentialResponse response = - getLockSettings().verifyTiedProfileChallenge(password, - isPattern ? CREDENTIAL_TYPE_PATTERN : CREDENTIAL_TYPE_PASSWORD, challenge, - userId); + getLockSettings().verifyTiedProfileChallenge( + credential.getCredential(), credential.getType(), challenge, userId); if (response.getResponseCode() == VerifyCredentialResponse.RESPONSE_OK) { return response.getPayload(); @@ -489,61 +462,6 @@ public class LockPatternUtils { } /** - * - * Check to see if a password matches the saved password. If no password exists, - * always returns true. - * @param password The password to check. - * @return Whether the password matches the stored one. - */ - @UnsupportedAppUsage - public boolean checkPassword(String password, int userId) throws RequestThrottledException { - byte[] passwordBytes = password != null ? password.getBytes() : null; - return checkPassword(passwordBytes, userId, null /* progressCallback */); - } - - - /** - * - * Check to see if a password matches the saved password. If no password exists, - * always returns true. - * @param password The password to check. - * @return Whether the password matches the stored one. - */ - public boolean checkPassword(byte[] password, int userId) throws RequestThrottledException { - return checkPassword(password, userId, null /* progressCallback */); - } - - // TODO(b/120484642): This method is necessary for vendor/qcom code and is a hidden api - /* * - * Check to see if a password matches the saved password. If no password exists, - * always returns true. - * @param password The password to check. - * @return Whether the password matches the stored one. - */ - public boolean checkPassword(String password, int userId, - @Nullable CheckCredentialProgressCallback progressCallback) - throws RequestThrottledException { - byte[] passwordBytes = password != null ? password.getBytes() : null; - throwIfCalledOnMainThread(); - return checkCredential(passwordBytes, CREDENTIAL_TYPE_PASSWORD, userId, progressCallback); - - } - - /** - * Check to see if a password matches the saved password. If no password exists, - * always returns true. - * @param password The password to check. - * @return Whether the password matches the stored one. - */ - - public boolean checkPassword(byte[] password, int userId, - @Nullable CheckCredentialProgressCallback progressCallback) - throws RequestThrottledException { - throwIfCalledOnMainThread(); - return checkCredential(password, CREDENTIAL_TYPE_PASSWORD, userId, progressCallback); - } - - /** * Check to see if vold already has the password. * Note that this also clears vold's copy of the password. * @return Whether the vold password matches or not. @@ -560,9 +478,10 @@ public class LockPatternUtils { * Returns the password history hash factor, needed to check new password against password * history with {@link #checkPasswordHistory(byte[], byte[], int)} */ - public byte[] getPasswordHistoryHashFactor(byte[] currentPassword, int userId) { + public byte[] getPasswordHistoryHashFactor(@NonNull LockscreenCredential currentPassword, + int userId) { try { - return getLockSettings().getHashFactor(currentPassword, userId); + return getLockSettings().getHashFactor(currentPassword.getCredential(), userId); } catch (RemoteException e) { Log.e(TAG, "failed to get hash factor", e); return null; @@ -679,57 +598,6 @@ public class LockPatternUtils { } /** - * Clear any lock pattern or password. - - * <p> This method will fail (returning {@code false}) if the previously - * saved password provided is incorrect, or if the lockscreen verification - * is still being throttled. - * - * @param savedCredential The previously saved credential - * @param userHandle the user whose pattern is to be saved. - * @return whether this was successful or not. - * @throws RuntimeException if password change encountered an unrecoverable error. - */ - public boolean clearLock(byte[] savedCredential, int userHandle) { - return clearLock(savedCredential, userHandle, false); - } - - /** - * Clear any lock pattern or password, with the option to ignore incorrect existing credential. - * <p> This method will fail (returning {@code false}) if the previously - * saved password provided is incorrect, or if the lockscreen verification - * is still being throttled. - * - * @param savedCredential The previously saved credential - * @param userHandle the user whose pattern is to be saved. - * @return whether this was successful or not. - * @throws RuntimeException if password change encountered an unrecoverable error. - */ - public boolean clearLock(byte[] savedCredential, int userHandle, boolean allowUntrustedChange) { - final int currentQuality = getKeyguardStoredPasswordQuality(userHandle); - setKeyguardStoredPasswordQuality(PASSWORD_QUALITY_UNSPECIFIED, userHandle); - - try { - if (!getLockSettings().setLockCredential(null, CREDENTIAL_TYPE_NONE, savedCredential, - PASSWORD_QUALITY_UNSPECIFIED, userHandle, allowUntrustedChange)) { - return false; - } - } catch (RemoteException | RuntimeException e) { - setKeyguardStoredPasswordQuality(currentQuality, userHandle); - throw new RuntimeException("Failed to clear lock", e); - } - - if (userHandle == UserHandle.USER_SYSTEM) { - // Set the encryption password to default. - updateEncryptionPassword(StorageManager.CRYPT_TYPE_DEFAULT, null); - setCredentialRequiredToDecrypt(false); - } - - onAfterChangingPassword(userHandle); - return true; - } - - /** * Disable showing lock screen at all for a given user. * This is only meaningful if pattern, pin or password are not set. * @@ -762,75 +630,88 @@ public class LockPatternUtils { || isDemoUser; } + /** Returns if the given quality maps to an alphabetic password */ + public static boolean isQualityAlphabeticPassword(int quality) { + return quality >= PASSWORD_QUALITY_ALPHABETIC; + } + + /** Returns if the given quality maps to an numeric pin */ + public static boolean isQualityNumericPin(int quality) { + return quality == PASSWORD_QUALITY_NUMERIC || quality == PASSWORD_QUALITY_NUMERIC_COMPLEX; + } + /** - * Save a lock pattern. + * Save a new lockscreen credential. * - * <p> This method will fail (returning {@code false}) if the previously saved pattern provided - * is incorrect, or if the lockscreen verification is still being throttled. + * <p> This method will fail (returning {@code false}) if the previously saved credential + * provided is incorrect, or if the lockscreen verification is still being throttled. + * + * @param newCredential The new credential to save + * @param savedCredential The current credential + * @param userId the user whose lockscreen credential is to be changed * - * @param pattern The new pattern to save. - * @param savedPattern The previously saved pattern, converted to byte[] format - * @param userId the user whose pattern is to be saved. - * @return whether this was successful or not. + * @return whether this method saved the new password successfully or not. This flow will fail + * and return false if the given credential is wrong. * @throws RuntimeException if password change encountered an unrecoverable error. */ - public boolean saveLockPattern(List<LockPatternView.Cell> pattern, byte[] savedPattern, - int userId) { - return saveLockPattern(pattern, savedPattern, userId, false); + public boolean setLockCredential(@NonNull LockscreenCredential newCredential, + @NonNull LockscreenCredential savedCredential, int userId) { + return setLockCredential(newCredential, savedCredential, userId, false); } /** - * Save a lock pattern. - * + * Save a new lockscreen credential. * <p> This method will fail (returning {@code false}) if the previously saved pattern provided - * is incorrect, or if the lockscreen verification is still being throttled. + * is incorrect and allowUntrustedChange is false, or if the lockscreen verification is still + * being throttled. + * @param newCredential The new credential to save + * @param savedCredential The current credential + * @param userHandle the user whose lockscreen credential is to be changed + * @param allowUntrustedChange whether we want to allow saving a new pattern if the existing + * credentialt being provided is incorrect. * - * @param pattern The new pattern to save. - * @param savedPattern The previously saved pattern, converted to byte[] format - * @param userId the user whose pattern is to be saved. - * @param allowUntrustedChange whether we want to allow saving a new password if the existing - * password being provided is incorrect. - * @return whether this was successful or not. + * @return whether this method saved the new password successfully or not. This flow will fail + * and return false if the given credential is wrong and allowUntrustedChange is false. * @throws RuntimeException if password change encountered an unrecoverable error. */ - public boolean saveLockPattern(List<LockPatternView.Cell> pattern, byte[] savedPattern, - int userId, boolean allowUntrustedChange) { + public boolean setLockCredential(@NonNull LockscreenCredential newCredential, + @NonNull LockscreenCredential savedCredential, int userHandle, + boolean allowUntrustedChange) { if (!hasSecureLockScreen()) { throw new UnsupportedOperationException( "This operation requires the lock screen feature."); } - if (pattern == null || pattern.size() < MIN_LOCK_PATTERN_SIZE) { - throw new IllegalArgumentException("pattern must not be null and at least " - + MIN_LOCK_PATTERN_SIZE + " dots long."); - } + newCredential.checkLength(); + + final int currentQuality = getKeyguardStoredPasswordQuality(userHandle); + setKeyguardStoredPasswordQuality(newCredential.getQuality(), userHandle); - final byte[] bytePattern = patternToByteArray(pattern); - final int currentQuality = getKeyguardStoredPasswordQuality(userId); - setKeyguardStoredPasswordQuality(PASSWORD_QUALITY_SOMETHING, userId); try { - if (!getLockSettings().setLockCredential(bytePattern, CREDENTIAL_TYPE_PATTERN, - savedPattern, PASSWORD_QUALITY_SOMETHING, userId, allowUntrustedChange)) { + if (!getLockSettings().setLockCredential( + newCredential.getCredential(), newCredential.getType(), + savedCredential.getCredential(), + newCredential.getQuality(), userHandle, allowUntrustedChange)) { + setKeyguardStoredPasswordQuality(currentQuality, userHandle); return false; } } catch (RemoteException | RuntimeException e) { - setKeyguardStoredPasswordQuality(currentQuality, userId); - throw new RuntimeException("Couldn't save lock pattern", e); - } - // Update the device encryption password. - if (userId == UserHandle.USER_SYSTEM - && LockPatternUtils.isDeviceEncryptionEnabled()) { - if (!shouldEncryptWithCredentials(true)) { - clearEncryptionPassword(); - } else { - updateEncryptionPassword(StorageManager.CRYPT_TYPE_PATTERN, bytePattern); - } + setKeyguardStoredPasswordQuality(currentQuality, userHandle); + throw new RuntimeException("Unable to save lock password", e); } - reportPatternWasChosen(userId); - onAfterChangingPassword(userId); + onPostPasswordChanged(newCredential, userHandle); return true; } + private void onPostPasswordChanged(LockscreenCredential newCredential, int userHandle) { + updateEncryptionPasswordIfNeeded(newCredential, userHandle); + if (newCredential.isPattern()) { + reportPatternWasChosen(userHandle); + } + updatePasswordHistory(newCredential, userHandle); + reportEnabledTrustAgentsChanged(userHandle); + } + private void updateCryptoUserInfo(int userId) { if (userId != UserHandle.USER_SYSTEM) { return; @@ -929,149 +810,35 @@ public class LockPatternUtils { } /** - * Save a lock password. Does not ensure that the password is as good - * as the requested mode, but will adjust the mode to be as good as the - * password. - * - * <p> This method will fail (returning {@code false}) if the previously - * saved password provided is incorrect, or if the lockscreen verification - * is still being throttled. - * - * @param password The password to save - * @param savedPassword The previously saved lock password, or null if none - * @param requestedQuality {@see DevicePolicyManager#getPasswordQuality( - * android.content.ComponentName)} - * @param userHandle The userId of the user to change the password for - * @return whether this was successful or not. - * @throws RuntimeException if password change encountered an unrecoverable error. - * @deprecated Pass password as a byte array - */ - @Deprecated - public boolean saveLockPassword(String password, String savedPassword, int requestedQuality, - int userHandle) { - byte[] passwordBytes = password != null ? password.getBytes() : null; - byte[] savedPasswordBytes = savedPassword != null ? savedPassword.getBytes() : null; - return saveLockPassword(passwordBytes, savedPasswordBytes, requestedQuality, userHandle); - } - - /** - * Save a lock password. Does not ensure that the password is as good - * as the requested mode, but will adjust the mode to be as good as the - * password. - * - * <p> This method will fail (returning {@code false}) if the previously - * saved password provided is incorrect, or if the lockscreen verification - * is still being throttled. - * - * @param password The password to save - * @param savedPassword The previously saved lock password, or null if none - * @param requestedQuality {@see DevicePolicyManager#getPasswordQuality( - * android.content.ComponentName)} - * @param userHandle The userId of the user to change the password for - * @return whether this was successful or not. - * @throws RuntimeException if password change encountered an unrecoverable error. - */ - public boolean saveLockPassword(byte[] password, byte[] savedPassword, int requestedQuality, - int userHandle) { - return saveLockPassword(password, savedPassword, requestedQuality, - userHandle, false); - } - - /** - * Save a lock password. Does not ensure that the password is as good - * as the requested mode, but will adjust the mode to be as good as the - * password. - * - * <p> This method will fail (returning {@code false}) if the previously - * saved password provided is incorrect, or if the lockscreen verification - * is still being throttled. - * - * @param password The password to save - * @param savedPassword The previously saved lock password, or null if none - * @param requestedQuality {@see DevicePolicyManager#getPasswordQuality( - * android.content.ComponentName)} - * @param userHandle The userId of the user to change the password for - * @param allowUntrustedChange whether we want to allow saving a new password if the existing - * password being provided is incorrect. - * @return whether this method saved the new password successfully or not. This flow will fail - * and return false if the given credential is wrong and allowUntrustedChange is false. - * @throws RuntimeException if password change encountered an unrecoverable error. - */ - public boolean saveLockPassword(byte[] password, byte[] savedPassword, - int requestedQuality, int userHandle, boolean allowUntrustedChange) { - if (!hasSecureLockScreen()) { - throw new UnsupportedOperationException( - "This operation requires the lock screen feature."); - } - if (password == null || password.length < MIN_LOCK_PASSWORD_SIZE) { - throw new IllegalArgumentException("password must not be null and at least " - + "of length " + MIN_LOCK_PASSWORD_SIZE); - } - - if (requestedQuality < PASSWORD_QUALITY_NUMERIC) { - throw new IllegalArgumentException("quality must be at least NUMERIC, but was " - + requestedQuality); - } - - final int currentQuality = getKeyguardStoredPasswordQuality(userHandle); - final int passwordQuality = PasswordMetrics.computeForPassword(password).quality; - final int newKeyguardQuality = - computeKeyguardQuality(CREDENTIAL_TYPE_PASSWORD, requestedQuality, passwordQuality); - setKeyguardStoredPasswordQuality(newKeyguardQuality, userHandle); - try { - getLockSettings().setLockCredential(password, CREDENTIAL_TYPE_PASSWORD, savedPassword, - requestedQuality, userHandle, allowUntrustedChange); - } catch (RemoteException | RuntimeException e) { - setKeyguardStoredPasswordQuality(currentQuality, userHandle); - throw new RuntimeException("Unable to save lock password", e); - } - - updateEncryptionPasswordIfNeeded(password, passwordQuality, userHandle); - updatePasswordHistory(password, userHandle); - onAfterChangingPassword(userHandle); - return true; - } - - /** - * Compute keyguard credential quality to store in PASSWORD_TYPE_KEY by computing max between - * them so that digit-only password is distinguished from PIN. - * - * TODO: remove this method and make CREDENTIAL_TYPE distinguish between PIN and password, so - * that this quality is no longer needs to be persisted. - */ - private int computeKeyguardQuality( - @CredentialType int credentialType, int requestedQuality, int passwordQuality) { - return credentialType == CREDENTIAL_TYPE_PASSWORD - ? Math.max(passwordQuality, requestedQuality) : passwordQuality; - } - - /** * Update device encryption password if calling user is USER_SYSTEM and device supports * encryption. */ - private void updateEncryptionPasswordIfNeeded(byte[] password, int quality, int userHandle) { + private void updateEncryptionPasswordIfNeeded(LockscreenCredential credential, int userHandle) { // Update the device encryption password. - if (userHandle == UserHandle.USER_SYSTEM - && LockPatternUtils.isDeviceEncryptionEnabled()) { - if (!shouldEncryptWithCredentials(true)) { - clearEncryptionPassword(); - } else { - boolean numeric = quality == PASSWORD_QUALITY_NUMERIC; - boolean numericComplex = quality == PASSWORD_QUALITY_NUMERIC_COMPLEX; - int type = numeric || numericComplex ? StorageManager.CRYPT_TYPE_PIN - : StorageManager.CRYPT_TYPE_PASSWORD; - updateEncryptionPassword(type, password); - } + if (userHandle != UserHandle.USER_SYSTEM || !isDeviceEncryptionEnabled()) { + return; } + if (!shouldEncryptWithCredentials(true)) { + updateEncryptionPassword(StorageManager.CRYPT_TYPE_DEFAULT, null); + return; + } + if (credential.isNone()) { + // Set the encryption password to default. + setCredentialRequiredToDecrypt(false); + } + updateEncryptionPassword(credential.getStorageCryptType(), credential.getCredential()); } /** * Store the hash of the *current* password in the password history list, if device policy * enforces password history requirement. */ - private void updatePasswordHistory(byte[] password, int userHandle) { - if (password == null || password.length == 0) { - Log.e(TAG, "checkPasswordHistory: empty password"); + private void updatePasswordHistory(LockscreenCredential password, int userHandle) { + if (password.isNone()) { + return; + } + if (password.isPattern()) { + // Do not keep track of historical patterns return; } // Add the password to the password history. We assume all @@ -1085,10 +852,10 @@ public class LockPatternUtils { passwordHistory = ""; } else { final byte[] hashFactor = getPasswordHistoryHashFactor(password, userHandle); - String hash = passwordToHistoryHash(password, hashFactor, userHandle); + String hash = passwordToHistoryHash(password.getCredential(), hashFactor, userHandle); if (hash == null) { Log.e(TAG, "Compute new style password hash failed, fallback to legacy style"); - hash = legacyPasswordToHash(password, userHandle); + hash = legacyPasswordToHash(password.getCredential(), userHandle); } if (TextUtils.isEmpty(passwordHistory)) { passwordHistory = hash; @@ -1132,8 +899,8 @@ public class LockPatternUtils { } /** - * Retrieves the quality mode for {@param userHandle}. - * {@see DevicePolicyManager#getPasswordQuality(android.content.ComponentName)} + * Retrieves the quality mode for {@code userHandle}. + * @see DevicePolicyManager#getPasswordQuality(android.content.ComponentName) * * @return stored password quality */ @@ -1147,37 +914,37 @@ public class LockPatternUtils { } /** - * Enables/disables the Separate Profile Challenge for this {@param userHandle}. This is a no-op + * Enables/disables the Separate Profile Challenge for this {@code userHandle}. This is a no-op * for user handles that do not belong to a managed profile. * * @param userHandle Managed profile user id * @param enabled True if separate challenge is enabled - * @param managedUserPassword Managed profile previous password. Null when {@param enabled} is + * @param managedUserPassword Managed profile previous password. Null when {@code enabled} is * true */ public void setSeparateProfileChallengeEnabled(int userHandle, boolean enabled, - byte[] managedUserPassword) { + LockscreenCredential managedUserPassword) { if (!isManagedProfile(userHandle)) { return; } try { getLockSettings().setSeparateProfileChallengeEnabled(userHandle, enabled, - managedUserPassword); - onAfterChangingPassword(userHandle); + managedUserPassword.getCredential()); + reportEnabledTrustAgentsChanged(userHandle); } catch (RemoteException e) { Log.e(TAG, "Couldn't update work profile challenge enabled"); } } /** - * Returns true if {@param userHandle} is a managed profile with separate challenge. + * Returns true if {@code userHandle} is a managed profile with separate challenge. */ public boolean isSeparateProfileChallengeEnabled(int userHandle) { return isManagedProfile(userHandle) && hasSeparateChallenge(userHandle); } /** - * Returns true if {@param userHandle} is a managed profile with unified challenge. + * Returns true if {@code userHandle} is a managed profile with unified challenge. */ public boolean isManagedProfileWithUnifiedChallenge(int userHandle) { return isManagedProfile(userHandle) && !hasSeparateChallenge(userHandle); @@ -1218,20 +985,6 @@ public class LockPatternUtils { /** * Deserialize a pattern. - * @param string The pattern serialized with {@link #patternToString} - * @return The pattern. - * @deprecated Pass patterns as byte[] and use byteArrayToPattern - */ - @Deprecated - public static List<LockPatternView.Cell> stringToPattern(String string) { - if (string == null) { - return null; - } - return byteArrayToPattern(string.getBytes()); - } - - /** - * Deserialize a pattern. * @param bytes The pattern serialized with {@link #patternToByteArray} * @return The pattern. */ @@ -1252,19 +1005,6 @@ public class LockPatternUtils { /** * Serialize a pattern. * @param pattern The pattern. - * @return The pattern in string form. - * @deprecated Use patternToByteArray instead. - */ - @UnsupportedAppUsage - @Deprecated - public static String patternToString(List<LockPatternView.Cell> pattern) { - return new String(patternToByteArray(pattern)); - } - - - /** - * Serialize a pattern. - * @param pattern The pattern. * @return The pattern in byte array form. */ public static byte[] patternToByteArray(List<LockPatternView.Cell> pattern) { @@ -1281,34 +1021,6 @@ public class LockPatternUtils { return res; } - /* - * Generate an SHA-1 hash for the pattern. Not the most secure, but it is - * at least a second level of protection. First level is that the file - * is in a location only readable by the system process. - * @param pattern the gesture pattern. - * @return the hash of the pattern in a byte array. - */ - @UnsupportedAppUsage - public static byte[] patternToHash(List<LockPatternView.Cell> pattern) { - if (pattern == null) { - return null; - } - - final int patternSize = pattern.size(); - byte[] res = new byte[patternSize]; - for (int i = 0; i < patternSize; i++) { - LockPatternView.Cell cell = pattern.get(i); - res[i] = (byte) (cell.getRow() * 3 + cell.getColumn()); - } - try { - MessageDigest md = MessageDigest.getInstance("SHA-1"); - byte[] hash = md.digest(res); - return hash; - } catch (NoSuchAlgorithmException nsa) { - return res; - } - } - private String getSalt(int userId) { long salt = getLong(LOCK_PASSWORD_SALT_KEY, 0, userId); if (salt == 0) { @@ -1332,6 +1044,7 @@ public class LockPatternUtils { * @param password the gesture pattern. * * @return the hash of the pattern in a byte array. + * TODO: move to LockscreenCredential class */ public String legacyPasswordToHash(byte[] password, int userId) { if (password == null || password.length == 0) { @@ -1362,6 +1075,7 @@ public class LockPatternUtils { /** * Hash the password for password history check purpose. + * TODO: move to LockscreenCredential class */ private String passwordToHistoryHash(byte[] passwordToHash, byte[] hashFactor, int userId) { if (passwordToHash == null || passwordToHash.length == 0 || hashFactor == null) { @@ -1629,7 +1343,7 @@ public class LockPatternUtils { } /** - * Disable trust until credentials have been entered for user {@param userId}. + * Disable trust until credentials have been entered for user {@code userId}. * * Requires the {@link android.Manifest.permission#ACCESS_KEYGUARD_SECURE_STORAGE} permission. * @@ -1640,7 +1354,7 @@ public class LockPatternUtils { } /** - * Requests strong authentication for user {@param userId}. + * Requests strong authentication for user {@code userId}. * * Requires the {@link android.Manifest.permission#ACCESS_KEYGUARD_SECURE_STORAGE} permission. * @@ -1657,7 +1371,7 @@ public class LockPatternUtils { } } - private void onAfterChangingPassword(int userHandle) { + private void reportEnabledTrustAgentsChanged(int userHandle) { getTrustManager().reportEnabledTrustAgentsChanged(userHandle); } @@ -1823,54 +1537,36 @@ public class LockPatternUtils { * <p>This method is only available to code running in the system server process itself. * * @param credential The new credential to be set - * @param type Credential type: password / pattern / none. - * @param requestedQuality the requested password quality by DevicePolicyManager. - * See {@link DevicePolicyManager#getPasswordQuality(android.content.ComponentName)} * @param tokenHandle Handle of the escrow token * @param token Escrow token - * @param userId The user who's lock credential to be changed + * @param userHandle The user who's lock credential to be changed * @return {@code true} if the operation is successful. */ - public boolean setLockCredentialWithToken(byte[] credential, int type, int requestedQuality, - long tokenHandle, byte[] token, int userId) { + public boolean setLockCredentialWithToken(LockscreenCredential credential, long tokenHandle, + byte[] token, int userHandle) { if (!hasSecureLockScreen()) { throw new UnsupportedOperationException( "This operation requires the lock screen feature."); } + credential.checkLength(); LockSettingsInternal localService = getLockSettingsInternal(); - if (type != CREDENTIAL_TYPE_NONE) { - if (credential == null || credential.length < MIN_LOCK_PASSWORD_SIZE) { - throw new IllegalArgumentException("password must not be null and at least " - + "of length " + MIN_LOCK_PASSWORD_SIZE); - } - final int quality = PasswordMetrics.computeForCredential(type, credential).quality; - final int keyguardQuality = computeKeyguardQuality(type, quality, requestedQuality); - if (!localService.setLockCredentialWithToken(credential, type, tokenHandle, token, - keyguardQuality, userId)) { - return false; - } - setKeyguardStoredPasswordQuality(quality, userId); - updateEncryptionPasswordIfNeeded(credential, quality, userId); - updatePasswordHistory(credential, userId); - onAfterChangingPassword(userId); - } else { - if (!(credential == null || credential.length == 0)) { - throw new IllegalArgumentException("password must be emtpy for NONE type"); - } - if (!localService.setLockCredentialWithToken(null, CREDENTIAL_TYPE_NONE, tokenHandle, - token, PASSWORD_QUALITY_UNSPECIFIED, userId)) { - return false; - } - setKeyguardStoredPasswordQuality(PASSWORD_QUALITY_UNSPECIFIED, userId); + final int currentQuality = getKeyguardStoredPasswordQuality(userHandle); + setKeyguardStoredPasswordQuality(credential.getQuality(), userHandle); - if (userId == UserHandle.USER_SYSTEM) { - // Set the encryption password to default. - updateEncryptionPassword(StorageManager.CRYPT_TYPE_DEFAULT, null); - setCredentialRequiredToDecrypt(false); + try { + if (!localService.setLockCredentialWithToken(credential.getCredential(), + credential.getType(), + tokenHandle, token, credential.getType(), userHandle)) { + setKeyguardStoredPasswordQuality(currentQuality, userHandle); + return false; } + } catch (RuntimeException e) { + setKeyguardStoredPasswordQuality(currentQuality, userHandle); + throw new RuntimeException("Unable to save lock credential", e); } - onAfterChangingPassword(userId); + + onPostPasswordChanged(credential, userHandle); return true; } @@ -1997,7 +1693,7 @@ public class LockPatternUtils { } /** - * @return true if unlocking with trust alone is allowed for {@param userId} by the current + * @return true if unlocking with trust alone is allowed for {@code userId} by the current * strong authentication requirements. */ public boolean isTrustAllowedForUser(int userId) { @@ -2005,7 +1701,7 @@ public class LockPatternUtils { } /** - * @return true if unlocking with a biometric method alone is allowed for {@param userId} + * @return true if unlocking with a biometric method alone is allowed for {@code userId} * by the current strong authentication requirements. */ public boolean isBiometricAllowedForUser(int userId) { @@ -2013,7 +1709,7 @@ public class LockPatternUtils { } /** - * Called when the strong authentication requirements for {@param userId} changed. + * Called when the strong authentication requirements for {@code userId} changed. */ public void onStrongAuthRequiredChanged(int userId) { } @@ -2102,22 +1798,4 @@ public class LockPatternUtils { return FRP_CREDENTIAL_ENABLED && context.getResources().getBoolean( com.android.internal.R.bool.config_enableCredentialFactoryResetProtection); } - - /** - * Converts a CharSequence to a byte array without requiring a toString(), which creates an - * additional copy. - * - * @param chars The CharSequence to convert - * @return A byte array representing the input - */ - public static byte[] charSequenceToByteArray(CharSequence chars) { - if (chars == null) { - return null; - } - byte[] bytes = new byte[chars.length()]; - for (int i = 0; i < chars.length(); i++) { - bytes[i] = (byte) chars.charAt(i); - } - return bytes; - } } diff --git a/core/java/com/android/internal/widget/LockPatternView.java b/core/java/com/android/internal/widget/LockPatternView.java index 3f6c4d4f5634..74a0aa37dafb 100644 --- a/core/java/com/android/internal/widget/LockPatternView.java +++ b/core/java/com/android/internal/widget/LockPatternView.java @@ -1318,7 +1318,7 @@ public class LockPatternView extends View { super.onRestoreInstanceState(ss.getSuperState()); setPattern( DisplayMode.Correct, - LockPatternUtils.stringToPattern(ss.getSerializedPattern())); + LockPatternUtils.byteArrayToPattern(ss.getSerializedPattern().getBytes())); mPatternDisplayMode = DisplayMode.values()[ss.getDisplayMode()]; mInputEnabled = ss.isInputEnabled(); mInStealthMode = ss.isInStealthMode(); diff --git a/core/java/com/android/internal/widget/LockscreenCredential.java b/core/java/com/android/internal/widget/LockscreenCredential.java new file mode 100644 index 000000000000..19e6d97eaa06 --- /dev/null +++ b/core/java/com/android/internal/widget/LockscreenCredential.java @@ -0,0 +1,339 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.widget; + +import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC; +import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_NUMERIC; +import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_SOMETHING; +import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED; + +import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_NONE; +import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PASSWORD; +import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PATTERN; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.os.Parcel; +import android.os.Parcelable; +import android.os.storage.StorageManager; +import android.text.TextUtils; + +import com.android.internal.util.Preconditions; + +import java.util.Arrays; +import java.util.List; + +/** + * A class representing a lockscreen credential. It can be either an empty password, a pattern + * or a password (or PIN). + * + * <p> As required by some security certification, the framework tries its best to + * remove copies of the lockscreen credential bytes from memory. In this regard, this class + * abuses the {@link AutoCloseable} interface for sanitizing memory. This + * presents a nice syntax to auto-zeroize memory with the try-with-resource statement: + * <pre> + * try {LockscreenCredential credential = LockscreenCredential.createPassword(...) { + * // Process the credential in some way + * } + * </pre> + * With this construct, we can garantee that there will be no copies of the password left in + * memory when the credential goes out of scope. This should help mitigate certain class of + * attacks where the attcker gains read-only access to full device memory (cold boot attack, + * unsecured software/hardware memory dumping interfaces such as JTAG). + */ +public class LockscreenCredential implements Parcelable, AutoCloseable { + + private final int mType; + // Stores raw credential bytes, or null if credential has been zeroized. An empty password + // is represented as a byte array of length 0. + private byte[] mCredential; + // Store the quality of the password, this is used to distinguish between pin + // (PASSWORD_QUALITY_NUMERIC) and password (PASSWORD_QUALITY_ALPHABETIC). + private final int mQuality; + + /** + * Private constructor, use static builder methods instead. + * + * <p> Builder methods should create a private copy of the credential bytes and pass in here. + * LockscreenCredential will only store the reference internally without copying. This is to + * minimize the number of extra copies introduced. + */ + private LockscreenCredential(int type, int quality, byte[] credential) { + Preconditions.checkNotNull(credential); + if (type == CREDENTIAL_TYPE_NONE) { + Preconditions.checkArgument(credential.length == 0); + } else { + Preconditions.checkArgument(credential.length > 0); + } + mType = type; + mQuality = quality; + mCredential = credential; + } + + /** + * Creates a LockscreenCredential object representing empty password. + */ + public static LockscreenCredential createNone() { + return new LockscreenCredential(CREDENTIAL_TYPE_NONE, PASSWORD_QUALITY_UNSPECIFIED, + new byte[0]); + } + + /** + * Creates a LockscreenCredential object representing the given pattern. + */ + public static LockscreenCredential createPattern(@NonNull List<LockPatternView.Cell> pattern) { + return new LockscreenCredential(CREDENTIAL_TYPE_PATTERN, + PASSWORD_QUALITY_SOMETHING, + LockPatternUtils.patternToByteArray(pattern)); + } + + /** + * Creates a LockscreenCredential object representing the given alphabetic password. + */ + public static LockscreenCredential createPassword(@NonNull CharSequence password) { + return new LockscreenCredential(CREDENTIAL_TYPE_PASSWORD, + PASSWORD_QUALITY_ALPHABETIC, + charSequenceToByteArray(password)); + } + + /** + * Creates a LockscreenCredential object representing the given numeric PIN. + */ + public static LockscreenCredential createPin(@NonNull CharSequence pin) { + return new LockscreenCredential(CREDENTIAL_TYPE_PASSWORD, + PASSWORD_QUALITY_NUMERIC, + charSequenceToByteArray(pin)); + } + + /** + * Creates a LockscreenCredential object representing the given alphabetic password. + * If the supplied password is empty, create an empty credential object. + */ + public static LockscreenCredential createPasswordOrNone(@Nullable CharSequence password) { + if (TextUtils.isEmpty(password)) { + return createNone(); + } else { + return createPassword(password); + } + } + + /** + * Creates a LockscreenCredential object representing the given numeric PIN. + * If the supplied password is empty, create an empty credential object. + */ + public static LockscreenCredential createPinOrNone(@Nullable CharSequence pin) { + if (TextUtils.isEmpty(pin)) { + return createNone(); + } else { + return createPin(pin); + } + } + + /** + * Create a LockscreenCredential object based on raw credential and type + * TODO: Remove once LSS.setUserPasswordMetrics accepts a LockscreenCredential + */ + public static LockscreenCredential createRaw(int type, byte[] credential) { + if (type == CREDENTIAL_TYPE_NONE) { + return createNone(); + } else { + return new LockscreenCredential(type, PASSWORD_QUALITY_UNSPECIFIED, credential); + } + } + + private void ensureNotZeroized() { + Preconditions.checkState(mCredential != null, "Credential is already zeroized"); + } + /** + * Returns the type of this credential. Can be one of {@link #CREDENTIAL_TYPE_NONE}, + * {@link #CREDENTIAL_TYPE_PATTERN} or {@link #CREDENTIAL_TYPE_PASSWORD}. + * + * TODO: Remove once credential type is internal. Callers should use {@link #isNone}, + * {@link #isPattern} and {@link #isPassword} instead. + */ + public int getType() { + ensureNotZeroized(); + return mType; + } + + /** + * Returns the quality type of the credential + */ + public int getQuality() { + ensureNotZeroized(); + return mQuality; + } + + /** + * Returns the credential bytes. This is a direct reference of the internal field so + * callers should not modify it. + * + */ + public byte[] getCredential() { + ensureNotZeroized(); + return mCredential; + } + + /** + * Returns the credential type recognized by {@link StorageManager}. Can be one of + * {@link StorageManager#CRYPT_TYPE_DEFAULT}, {@link StorageManager#CRYPT_TYPE_PATTERN}, + * {@link StorageManager#CRYPT_TYPE_PIN} or {@link StorageManager#CRYPT_TYPE_PASSWORD}. + */ + public int getStorageCryptType() { + if (isNone()) { + return StorageManager.CRYPT_TYPE_DEFAULT; + } + if (isPattern()) { + return StorageManager.CRYPT_TYPE_PATTERN; + } + if (isPassword()) { + return mQuality == PASSWORD_QUALITY_NUMERIC + ? StorageManager.CRYPT_TYPE_PIN : StorageManager.CRYPT_TYPE_PASSWORD; + } + throw new IllegalStateException("Unhandled credential type"); + } + + /** Returns whether this is an empty credential */ + public boolean isNone() { + ensureNotZeroized(); + return mType == CREDENTIAL_TYPE_NONE; + } + + /** Returns whether this is a pattern credential */ + public boolean isPattern() { + ensureNotZeroized(); + return mType == CREDENTIAL_TYPE_PATTERN; + } + + /** Returns whether this is a password credential */ + public boolean isPassword() { + ensureNotZeroized(); + return mType == CREDENTIAL_TYPE_PASSWORD; + } + + /** Returns the length of the credential */ + public int size() { + ensureNotZeroized(); + return mCredential.length; + } + + /** Create a copy of the credential */ + public LockscreenCredential duplicate() { + return new LockscreenCredential(mType, mQuality, + mCredential != null ? Arrays.copyOf(mCredential, mCredential.length) : null); + } + + /** + * Zeroize the credential bytes. + */ + public void zeroize() { + if (mCredential != null) { + Arrays.fill(mCredential, (byte) 0); + mCredential = null; + } + } + + /** + * Check if the credential meets minimal length requirement. + * + * @throws IllegalArgumentException if the credential is too short. + */ + public void checkLength() { + if (isNone()) { + return; + } + if (isPattern()) { + if (size() < LockPatternUtils.MIN_LOCK_PATTERN_SIZE) { + throw new IllegalArgumentException("pattern must not be null and at least " + + LockPatternUtils.MIN_LOCK_PATTERN_SIZE + " dots long."); + } + return; + } + if (isPassword()) { + if (size() < LockPatternUtils.MIN_LOCK_PASSWORD_SIZE) { + throw new IllegalArgumentException("password must not be null and at least " + + "of length " + LockPatternUtils.MIN_LOCK_PASSWORD_SIZE); + } + return; + } + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(mType); + dest.writeInt(mQuality); + dest.writeByteArray(mCredential); + } + + public static final Parcelable.Creator<LockscreenCredential> CREATOR = + new Parcelable.Creator<LockscreenCredential>() { + + @Override + public LockscreenCredential createFromParcel(Parcel source) { + return new LockscreenCredential(source.readInt(), source.readInt(), + source.createByteArray()); + } + + @Override + public LockscreenCredential[] newArray(int size) { + return new LockscreenCredential[size]; + } + }; + + @Override + public int describeContents() { + return 0; + } + + @Override + public void close() { + zeroize(); + } + + @Override + public int hashCode() { + // Effective Java — Item 9 + return ((17 + mType) * 31 + mQuality) * 31 + mCredential.hashCode(); + } + + @Override + public boolean equals(Object o) { + if (o == this) return true; + if (!(o instanceof LockscreenCredential)) return false; + final LockscreenCredential other = (LockscreenCredential) o; + return mType == other.mType && mQuality == other.mQuality + && Arrays.equals(mCredential, other.mCredential); + } + + /** + * Converts a CharSequence to a byte array without requiring a toString(), which creates an + * additional copy. + * + * @param chars The CharSequence to convert + * @return A byte array representing the input + */ + private static byte[] charSequenceToByteArray(CharSequence chars) { + if (chars == null) { + return new byte[0]; + } + byte[] bytes = new byte[chars.length()]; + for (int i = 0; i < chars.length(); i++) { + bytes[i] = (byte) chars.charAt(i); + } + return bytes; + } +} diff --git a/core/java/com/android/server/SystemConfig.java b/core/java/com/android/server/SystemConfig.java index 697825dcefd7..ea0389f49a45 100644 --- a/core/java/com/android/server/SystemConfig.java +++ b/core/java/com/android/server/SystemConfig.java @@ -168,6 +168,10 @@ public class SystemConfig { // These are the permitted backup transport service components final ArraySet<ComponentName> mBackupTransportWhitelist = new ArraySet<>(); + // These are packages mapped to maps of component class name to default enabled state. + final ArrayMap<String, ArrayMap<String, Boolean>> mPackageComponentEnabledState = + new ArrayMap<>(); + // Package names that are exempted from private API blacklisting final ArraySet<String> mHiddenApiPackageWhitelist = new ArraySet<>(); @@ -301,6 +305,10 @@ public class SystemConfig { return mBackupTransportWhitelist; } + public ArrayMap<String, Boolean> getComponentsEnabledStates(String packageName) { + return mPackageComponentEnabledState.get(packageName); + } + public ArraySet<String> getDisabledUntilUsedPreinstalledCarrierApps() { return mDisabledUntilUsedPreinstalledCarrierApps; } @@ -846,6 +854,14 @@ public class SystemConfig { } XmlUtils.skipCurrentTag(parser); } break; + case "component-override": { + if (allowAppConfigs) { + readComponentOverrides(parser, permFile); + } else { + logNotAllowedInPartition(name, permFile, parser); + } + XmlUtils.skipCurrentTag(parser); + } break; case "backup-transport-whitelisted-service": { if (allowFeatures) { String serviceName = parser.getAttributeValue(null, "service"); @@ -1269,6 +1285,54 @@ public class SystemConfig { } } + private void readComponentOverrides(XmlPullParser parser, File permFile) + throws IOException, XmlPullParserException { + String pkgname = parser.getAttributeValue(null, "package"); + if (pkgname == null) { + Slog.w(TAG, "<component-override> without package in " + + permFile + " at " + parser.getPositionDescription()); + return; + } + + pkgname = pkgname.intern(); + + final int depth = parser.getDepth(); + while (XmlUtils.nextElementWithin(parser, depth)) { + String name = parser.getName(); + if ("component".equals(name)) { + String clsname = parser.getAttributeValue(null, "class"); + String enabled = parser.getAttributeValue(null, "enabled"); + if (clsname == null) { + Slog.w(TAG, "<component> without class in " + + permFile + " at " + parser.getPositionDescription()); + return; + } else if (enabled == null) { + Slog.w(TAG, "<component> without enabled in " + + permFile + " at " + parser.getPositionDescription()); + return; + } + + if (clsname.startsWith(".")) { + clsname = pkgname + clsname; + } + + clsname = clsname.intern(); + + ArrayMap<String, Boolean> componentEnabledStates = + mPackageComponentEnabledState.get(pkgname); + if (componentEnabledStates == null) { + componentEnabledStates = new ArrayMap<>(); + mPackageComponentEnabledState.put(pkgname, + componentEnabledStates); + } + + componentEnabledStates.put(clsname, !"false".equals(enabled)); + } else { + XmlUtils.skipCurrentTag(parser); + } + } + } + private static boolean isSystemProcess() { return Process.myUid() == Process.SYSTEM_UID; } diff --git a/core/jni/android_view_InputChannel.cpp b/core/jni/android_view_InputChannel.cpp index af34e7b7a7ff..bf1cea8cff2e 100644 --- a/core/jni/android_view_InputChannel.cpp +++ b/core/jni/android_view_InputChannel.cpp @@ -112,7 +112,9 @@ void android_view_InputChannel_setDisposeCallback(JNIEnv* env, jobject inputChan } static jobject android_view_InputChannel_createInputChannel(JNIEnv* env, - std::unique_ptr<NativeInputChannel> nativeInputChannel) { + sp<InputChannel> inputChannel) { + std::unique_ptr<NativeInputChannel> nativeInputChannel = + std::make_unique<NativeInputChannel>(inputChannel); jobject inputChannelObj = env->NewObject(gInputChannelClassInfo.clazz, gInputChannelClassInfo.ctor); if (inputChannelObj) { @@ -143,14 +145,12 @@ static jobjectArray android_view_InputChannel_nativeOpenInputChannelPair(JNIEnv* return nullptr; } - jobject serverChannelObj = android_view_InputChannel_createInputChannel(env, - std::make_unique<NativeInputChannel>(serverChannel)); + jobject serverChannelObj = android_view_InputChannel_createInputChannel(env, serverChannel); if (env->ExceptionCheck()) { return nullptr; } - jobject clientChannelObj = android_view_InputChannel_createInputChannel(env, - std::make_unique<NativeInputChannel>(clientChannel)); + jobject clientChannelObj = android_view_InputChannel_createInputChannel(env, clientChannel); if (env->ExceptionCheck()) { return nullptr; } diff --git a/core/proto/android/service/procstats.proto b/core/proto/android/service/procstats.proto index f49a04422c0e..ad7299d9a45c 100644 --- a/core/proto/android/service/procstats.proto +++ b/core/proto/android/service/procstats.proto @@ -241,12 +241,25 @@ message PackageAssociationSourceProcessStatsProto { repeated StateStats active_state_stats = 6; } -// Next Tag: 3 +// Next Tag: 7 message PackageAssociationProcessStatsProto { option (android.msg_privacy).dest = DEST_AUTOMATIC; // Name of the target component. optional string component_name = 1; + + // Total count of the times this association appeared. + optional int32 total_count = 3; + + // Millisecond uptime total duration this association was around. + optional int64 total_duration_ms = 4; + + // Total count of the times this association became actively impacting its target process. + optional int32 active_count = 5; + + // Millisecond uptime total duration this association was around. + optional int64 active_duration_ms = 6; + // Information on one source in this association. repeated PackageAssociationSourceProcessStatsProto sources = 2; } diff --git a/core/proto/android/stats/devicepolicy/device_policy_enums.proto b/core/proto/android/stats/devicepolicy/device_policy_enums.proto index 0821d147044d..15813a1b2a72 100644 --- a/core/proto/android/stats/devicepolicy/device_policy_enums.proto +++ b/core/proto/android/stats/devicepolicy/device_policy_enums.proto @@ -113,9 +113,9 @@ enum EventId { PROVISIONING_PREPROVISIONING_ACTIVITY_TIME_MS = 87; PROVISIONING_ENCRYPT_DEVICE_ACTIVITY_TIME_MS = 88; PROVISIONING_WEB_ACTIVITY_TIME_MS = 89; - PROVISIONING_TRAMPOLINE_ACTIVITY_TIME_MS = 90; - PROVISIONING_POST_ENCRYPTION_ACTIVITY_TIME_MS = 91; - PROVISIONING_FINALIZATION_ACTIVITY_TIME_MS = 92; + PROVISIONING_TRAMPOLINE_ACTIVITY_TIME_MS = 90 [deprecated=true]; + PROVISIONING_POST_ENCRYPTION_ACTIVITY_TIME_MS = 91 [deprecated=true]; + PROVISIONING_FINALIZATION_ACTIVITY_TIME_MS = 92 [deprecated=true]; PROVISIONING_NETWORK_TYPE = 93; PROVISIONING_ACTION = 94; PROVISIONING_EXTRAS = 95; diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index b3372a6eea06..5c1e13b27f9d 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -2955,7 +2955,7 @@ @hide --> <permission android:name="android.permission.INTERNAL_SYSTEM_WINDOW" - android:protectionLevel="signature|telephony" /> + android:protectionLevel="signature|telephony|wifi" /> <!-- @SystemApi Allows an application to use {@link android.view.WindowManager.LayoutsParams#SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS} diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml index 166cde0d7005..ffcfe4310f06 100644 --- a/core/res/res/values/attrs_manifest.xml +++ b/core/res/res/values/attrs_manifest.xml @@ -298,6 +298,9 @@ <!-- Additional flag from base permission type: this permission can be automatically granted to the system telephony apps --> <flag name="telephony" value="0x400000" /> + <!-- Additional flag from base permission type: this permission can be automatically + granted to the system wifi app--> + <flag name="wifi" value="0x800000" /> </attr> <!-- Flags indicating more context for a permission group. --> diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index 531023fb41bf..56052464af06 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -3687,7 +3687,6 @@ --> <string name="config_defaultWellbeingPackage" translatable="false"></string> - <!-- The package name for the system telephony apps. This package must be trusted, as it will be granted with permissions with special telephony protection level. Note, framework by default support multiple telephony apps, each package @@ -3696,6 +3695,13 @@ --> <string name="config_telephonyPackages" translatable="false">"com.android.phone,com.android.stk,com.android.providers.telephony,com.android.ons"</string> + <!-- The package name for the default system wifi app. + This package must be trusted, as it has the permissions to control wifi + connectivity on the device. + Example: "com.android.wifi" + --> + <string name="config_wifiPackage" translatable="false">"com.android.wifi"</string> + <!-- The component name for the default system attention service. This service must be trusted, as it can be activated without explicit consent of the user. See android.attention.AttentionManagerService. diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 06fc5e356c48..42cd2cdad336 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -3468,6 +3468,7 @@ <java-symbol type="string" name="config_defaultTextClassifierPackage" /> <java-symbol type="string" name="config_defaultWellbeingPackage" /> <java-symbol type="string" name="config_telephonyPackages" /> + <java-symbol type="string" name="config_wifiPackage" /> <java-symbol type="string" name="config_defaultContentCaptureService" /> <java-symbol type="string" name="config_defaultAugmentedAutofillService" /> <java-symbol type="string" name="config_defaultAppPredictionService" /> diff --git a/core/tests/coretests/src/android/util/TimestampedValueTest.java b/core/tests/coretests/src/android/util/TimestampedValueTest.java index 6e3ab796c5d3..6fc2400316c2 100644 --- a/core/tests/coretests/src/android/util/TimestampedValueTest.java +++ b/core/tests/coretests/src/android/util/TimestampedValueTest.java @@ -55,12 +55,12 @@ public class TimestampedValueTest { TimestampedValue<String> stringValue = new TimestampedValue<>(1000, "Hello"); Parcel parcel = Parcel.obtain(); try { - TimestampedValue.writeToParcel(parcel, stringValue); + parcel.writeParcelable(stringValue, 0); parcel.setDataPosition(0); TimestampedValue<String> stringValueCopy = - TimestampedValue.readFromParcel(parcel, null /* classLoader */, String.class); + parcel.readParcelable(null /* classLoader */); assertEquals(stringValue, stringValueCopy); } finally { parcel.recycle(); @@ -72,12 +72,12 @@ public class TimestampedValueTest { TimestampedValue<String> stringValue = new TimestampedValue<>(1000, "Hello"); Parcel parcel = Parcel.obtain(); try { - TimestampedValue.writeToParcel(parcel, stringValue); + parcel.writeParcelable(stringValue, 0); parcel.setDataPosition(0); - TimestampedValue<Object> stringValueCopy = - TimestampedValue.readFromParcel(parcel, null /* classLoader */, Object.class); + TimestampedValue<String> stringValueCopy = + parcel.readParcelable(null /* classLoader */); assertEquals(stringValue, stringValueCopy); } finally { parcel.recycle(); @@ -85,15 +85,15 @@ public class TimestampedValueTest { } @Test - public void testParceling_valueClassIncompatible() { - TimestampedValue<String> stringValue = new TimestampedValue<>(1000, "Hello"); + public void testParceling_valueClassNotParcelable() { + // This class is not one supported by Parcel.writeValue(). + class NotParcelable {} + + TimestampedValue<NotParcelable> notParcelableValue = + new TimestampedValue<>(1000, new NotParcelable()); Parcel parcel = Parcel.obtain(); try { - TimestampedValue.writeToParcel(parcel, stringValue); - - parcel.setDataPosition(0); - - TimestampedValue.readFromParcel(parcel, null /* classLoader */, Double.class); + parcel.writeParcelable(notParcelableValue, 0); fail(); } catch (RuntimeException expected) { } finally { @@ -106,12 +106,11 @@ public class TimestampedValueTest { TimestampedValue<String> nullValue = new TimestampedValue<>(1000, null); Parcel parcel = Parcel.obtain(); try { - TimestampedValue.writeToParcel(parcel, nullValue); + parcel.writeParcelable(nullValue, 0); parcel.setDataPosition(0); - TimestampedValue<Object> nullValueCopy = - TimestampedValue.readFromParcel(parcel, null /* classLoader */, String.class); + TimestampedValue<String> nullValueCopy = parcel.readParcelable(null /* classLoader */); assertEquals(nullValue, nullValueCopy); } finally { parcel.recycle(); diff --git a/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutControllerTest.java b/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutControllerTest.java index abee1da2ed7a..7b4054348642 100644 --- a/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutControllerTest.java +++ b/core/tests/coretests/src/com/android/internal/accessibility/AccessibilityShortcutControllerTest.java @@ -345,9 +345,9 @@ public class AccessibilityShortcutControllerTest { accessibilityShortcutController.performAccessibilityShortcut(); accessibilityShortcutController.performAccessibilityShortcut(); verify(mToast).show(); - assertEquals(WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS, + assertEquals(WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS, mLayoutParams.privateFlags - & WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS); + & WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS); verify(mAccessibilityManagerService, times(1)).performAccessibilityShortcut(); } diff --git a/core/tests/coretests/src/com/android/internal/widget/LockscreenCredentialTest.java b/core/tests/coretests/src/com/android/internal/widget/LockscreenCredentialTest.java new file mode 100644 index 000000000000..5eec91c8840b --- /dev/null +++ b/core/tests/coretests/src/com/android/internal/widget/LockscreenCredentialTest.java @@ -0,0 +1,155 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.widget; + + +import android.test.AndroidTestCase; + +import java.util.Arrays; + + +public class LockscreenCredentialTest extends AndroidTestCase { + + public void testEmptyCredential() { + LockscreenCredential empty = LockscreenCredential.createNone(); + + assertTrue(empty.isNone()); + assertEquals(0, empty.size()); + assertNotNull(empty.getCredential()); + + assertFalse(empty.isPassword()); + assertFalse(empty.isPattern()); + } + + public void testPasswordCredential() { + LockscreenCredential password = LockscreenCredential.createPassword("password"); + + assertTrue(password.isPassword()); + assertEquals(8, password.size()); + assertTrue(Arrays.equals("password".getBytes(), password.getCredential())); + + assertFalse(password.isNone()); + assertFalse(password.isPattern()); + } + + public void testPatternCredential() { + LockscreenCredential pattern = LockscreenCredential.createPattern(Arrays.asList( + LockPatternView.Cell.of(0, 0), + LockPatternView.Cell.of(0, 1), + LockPatternView.Cell.of(0, 2), + LockPatternView.Cell.of(1, 2), + LockPatternView.Cell.of(2, 2) + )); + + assertTrue(pattern.isPattern()); + assertEquals(5, pattern.size()); + assertTrue(Arrays.equals("12369".getBytes(), pattern.getCredential())); + + assertFalse(pattern.isNone()); + assertFalse(pattern.isPassword()); + } + + public void testPasswordOrNoneCredential() { + assertEquals(LockscreenCredential.createNone(), + LockscreenCredential.createPasswordOrNone(null)); + assertEquals(LockscreenCredential.createNone(), + LockscreenCredential.createPasswordOrNone("")); + assertEquals(LockscreenCredential.createPassword("abcd"), + LockscreenCredential.createPasswordOrNone("abcd")); + } + + public void testSanitize() { + LockscreenCredential password = LockscreenCredential.createPassword("password"); + password.zeroize(); + + try { + password.isNone(); + fail("Sanitized credential still accessible"); + } catch (IllegalStateException expected) { } + + try { + password.isPattern(); + fail("Sanitized credential still accessible"); + } catch (IllegalStateException expected) { } + try { + password.isPassword(); + fail("Sanitized credential still accessible"); + } catch (IllegalStateException expected) { } + try { + password.size(); + fail("Sanitized credential still accessible"); + } catch (IllegalStateException expected) { } + try { + password.getCredential(); + fail("Sanitized credential still accessible"); + } catch (IllegalStateException expected) { } + } + + public void testEquals() { + assertEquals(LockscreenCredential.createNone(), LockscreenCredential.createNone()); + assertEquals(LockscreenCredential.createPassword("1234"), + LockscreenCredential.createPassword("1234")); + assertEquals(LockscreenCredential.createPin("4321"), + LockscreenCredential.createPin("4321")); + assertEquals(createPattern("1234"), createPattern("1234")); + + assertNotSame(LockscreenCredential.createPassword("1234"), + LockscreenCredential.createNone()); + assertNotSame(LockscreenCredential.createPassword("1234"), + LockscreenCredential.createPassword("4321")); + assertNotSame(LockscreenCredential.createPassword("1234"), + createPattern("1234")); + assertNotSame(LockscreenCredential.createPassword("1234"), + LockscreenCredential.createPin("1234")); + + assertNotSame(LockscreenCredential.createPin("1111"), + LockscreenCredential.createNone()); + assertNotSame(LockscreenCredential.createPin("1111"), + LockscreenCredential.createPin("2222")); + assertNotSame(LockscreenCredential.createPin("1111"), + createPattern("1111")); + assertNotSame(LockscreenCredential.createPin("1111"), + LockscreenCredential.createPassword("1111")); + + assertNotSame(createPattern("5678"), + LockscreenCredential.createNone()); + assertNotSame(createPattern("5678"), + createPattern("1234")); + assertNotSame(createPattern("5678"), + LockscreenCredential.createPassword("5678")); + assertNotSame(createPattern("5678"), + LockscreenCredential.createPin("5678")); + } + + public void testDuplicate() { + LockscreenCredential credential; + + credential = LockscreenCredential.createNone(); + assertEquals(credential, credential.duplicate()); + credential = LockscreenCredential.createPassword("abcd"); + assertEquals(credential, credential.duplicate()); + credential = LockscreenCredential.createPin("1234"); + assertEquals(credential, credential.duplicate()); + credential = createPattern("5678"); + assertEquals(credential, credential.duplicate()); + } + + private LockscreenCredential createPattern(String patternString) { + return LockscreenCredential.createPattern(LockPatternUtils.byteArrayToPattern( + patternString.getBytes())); + } +} diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml index 10f6b6925431..4b4e416fd9d6 100644 --- a/data/etc/privapp-permissions-platform.xml +++ b/data/etc/privapp-permissions-platform.xml @@ -121,6 +121,7 @@ applications that come with the platform <permission name="android.permission.APPROVE_INCIDENT_REPORTS"/> <permission name="android.permission.READ_PRIVILEGED_PHONE_STATE" /> <permission name="android.permission.SUBSTITUTE_NOTIFICATION_APP_NAME" /> + <permission name="android.permission.PACKAGE_USAGE_STATS" /> </privapp-permissions> <privapp-permissions package="com.android.phone"> @@ -359,6 +360,7 @@ applications that come with the platform <permission name="android.permission.CONNECTIVITY_INTERNAL"/> <permission name="android.permission.DUMP"/> <permission name="android.permission.INTERACT_ACROSS_USERS"/> + <permission name="android.permission.INTERNAL_SYSTEM_WINDOW"/> <permission name="android.permission.LOCAL_MAC_ADDRESS"/> <permission name="android.permission.MANAGE_USERS"/> <permission name="android.permission.PACKAGE_USAGE_STATS"/> diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp index ae90f117d448..61b72cf9d193 100644 --- a/libs/hwui/Android.bp +++ b/libs/hwui/Android.bp @@ -26,8 +26,10 @@ cc_defaults { // a problem "-Wno-free-nonheap-object", - // clang's warning is broken, see: https://llvm.org/bugs/show_bug.cgi?id=21629 - "-Wno-missing-braces", + // Clang is producing non-determistic binary when the new pass manager is + // enabled. Disable the new PM as a temporary workaround. + // b/142372146 + "-fno-experimental-new-pass-manager", ], include_dirs: [ diff --git a/libs/hwui/DamageAccumulator.h b/libs/hwui/DamageAccumulator.h index 7d0b6877a71a..030a20f31c42 100644 --- a/libs/hwui/DamageAccumulator.h +++ b/libs/hwui/DamageAccumulator.h @@ -27,7 +27,7 @@ // Smaller than INT_MIN/INT_MAX because we offset these values // and thus don't want to be adding offsets to INT_MAX, that's bad #define DIRTY_MIN (-0x7ffffff - 1) -#define DIRTY_MAX (0x7ffffff) +#define DIRTY_MAX (0x8000000) namespace android { namespace uirenderer { diff --git a/location/java/android/location/AbstractListenerManager.java b/location/java/android/location/AbstractListenerManager.java index c41023e31065..944ebf937dc8 100644 --- a/location/java/android/location/AbstractListenerManager.java +++ b/location/java/android/location/AbstractListenerManager.java @@ -42,8 +42,8 @@ abstract class AbstractListenerManager<T> { @Nullable private volatile T mListener; private Registration(Executor executor, T listener) { - Preconditions.checkArgument(listener != null); - Preconditions.checkArgument(executor != null); + Preconditions.checkArgument(listener != null, "invalid null listener/callback"); + Preconditions.checkArgument(executor != null, "invalid null executor"); mExecutor = executor; mListener = listener; } @@ -83,16 +83,18 @@ abstract class AbstractListenerManager<T> { return addInternal(listener, executor); } - protected final boolean addInternal(Object listener, Handler handler) throws RemoteException { + protected final boolean addInternal(@NonNull Object listener, @NonNull Handler handler) + throws RemoteException { return addInternal(listener, new HandlerExecutor(handler)); } - protected final boolean addInternal(Object listener, Executor executor) throws RemoteException { + protected final boolean addInternal(@NonNull Object listener, @NonNull Executor executor) + throws RemoteException { + Preconditions.checkArgument(listener != null, "invalid null listener/callback"); return addInternal(listener, new Registration<>(executor, convertKey(listener))); } private boolean addInternal(Object key, Registration<T> registration) throws RemoteException { - Preconditions.checkNotNull(key); Preconditions.checkNotNull(registration); synchronized (mListeners) { diff --git a/location/java/android/location/ILocationManager.aidl b/location/java/android/location/ILocationManager.aidl index 7f650e36dcd3..daa2e08a6780 100644 --- a/location/java/android/location/ILocationManager.aidl +++ b/location/java/android/location/ILocationManager.aidl @@ -42,11 +42,11 @@ import com.android.internal.location.ProviderProperties; interface ILocationManager { void requestLocationUpdates(in LocationRequest request, in ILocationListener listener, - in PendingIntent intent, String packageName); + in PendingIntent intent, String packageName, String listenerIdentifier); void removeUpdates(in ILocationListener listener, in PendingIntent intent, String packageName); void requestGeofence(in LocationRequest request, in Geofence geofence, - in PendingIntent intent, String packageName); + in PendingIntent intent, String packageName, String listenerIdentifier); void removeGeofence(in Geofence fence, in PendingIntent intent, String packageName); Location getLastLocation(in LocationRequest request, String packageName); @@ -64,22 +64,23 @@ interface ILocationManager boolean sendNiResponse(int notifId, int userResponse); - boolean addGnssMeasurementsListener(in IGnssMeasurementsListener listener, in String packageName); + boolean addGnssMeasurementsListener(in IGnssMeasurementsListener listener, + String packageName, String listenerIdentifier); void injectGnssMeasurementCorrections(in GnssMeasurementCorrections corrections, in String packageName); long getGnssCapabilities(in String packageName); void removeGnssMeasurementsListener(in IGnssMeasurementsListener listener); - boolean addGnssNavigationMessageListener( - in IGnssNavigationMessageListener listener, - in String packageName); + boolean addGnssNavigationMessageListener(in IGnssNavigationMessageListener listener, + String packageName, String listenerIdentifier); void removeGnssNavigationMessageListener(in IGnssNavigationMessageListener listener); int getGnssYearOfHardware(); String getGnssHardwareModelName(); int getGnssBatchSize(String packageName); - boolean addGnssBatchingCallback(in IBatchedLocationCallback callback, String packageName); + boolean addGnssBatchingCallback(in IBatchedLocationCallback callback, String packageName, + String listenerIdentifier); void removeGnssBatchingCallback(); boolean startGnssBatch(long periodNanos, boolean wakeOnFifoFull, String packageName); void flushGnssBatch(String packageName); diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java index 5a0700d7e572..0b3e1c3f57d3 100644 --- a/location/java/android/location/LocationManager.java +++ b/location/java/android/location/LocationManager.java @@ -543,6 +543,23 @@ public class LocationManager { } /** + * Create a string that allows an app to identify a listener + * + * @param listener The listener + * + * @return A identifying string + */ + private static String getListenerIdentifier(@NonNull Object listener) { + StringBuilder sb = new StringBuilder(); + + sb.append(listener.getClass().getName()); + sb.append('@'); + sb.append(Integer.toHexString(System.identityHashCode(listener))); + + return sb.toString(); + } + + /** * Register for a single location update using the named provider and * a callback. * @@ -982,7 +999,7 @@ public class LocationManager { boolean registered = false; try { mService.requestLocationUpdates(locationRequest, transport, null, - mContext.getPackageName()); + mContext.getPackageName(), getListenerIdentifier(listener)); registered = true; } catch (RemoteException e) { throw e.rethrowFromSystemServer(); @@ -1027,7 +1044,7 @@ public class LocationManager { try { mService.requestLocationUpdates(locationRequest, null, pendingIntent, - mContext.getPackageName()); + mContext.getPackageName(), getListenerIdentifier(pendingIntent)); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -1490,7 +1507,8 @@ public class LocationManager { Geofence fence = Geofence.createCircle(latitude, longitude, radius); LocationRequest request = new LocationRequest().setExpireIn(expiration); try { - mService.requestGeofence(request, fence, intent, mContext.getPackageName()); + mService.requestGeofence(request, fence, intent, mContext.getPackageName(), + getListenerIdentifier(intent)); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -1567,7 +1585,8 @@ public class LocationManager { Preconditions.checkArgument(fence != null, "invalid null geofence"); try { - mService.requestGeofence(request, fence, intent, mContext.getPackageName()); + mService.requestGeofence(request, fence, intent, mContext.getPackageName(), + getListenerIdentifier(intent)); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -1781,6 +1800,7 @@ public class LocationManager { * @param handler a handler with a looper that the callback runs on * @return true if the listener was successfully added * + * @throws IllegalArgumentException if callback is null * @throws SecurityException if the ACCESS_FINE_LOCATION permission is not present */ @RequiresPermission(ACCESS_FINE_LOCATION) @@ -1800,10 +1820,12 @@ public class LocationManager { /** * Registers a GNSS status callback. * - * @param callback GNSS status callback object to register * @param executor the executor that the callback runs on + * @param callback GNSS status callback object to register * @return true if the listener was successfully added * + * @throws IllegalArgumentException if executor is null + * @throws IllegalArgumentException if callback is null * @throws SecurityException if the ACCESS_FINE_LOCATION permission is not present */ @RequiresPermission(ACCESS_FINE_LOCATION) @@ -1872,6 +1894,8 @@ public class LocationManager { * @param listener a {@link OnNmeaMessageListener} object to register * @param handler a handler with the looper that the listener runs on. * @return true if the listener was successfully added + * + * @throws IllegalArgumentException if listener is null * @throws SecurityException if the ACCESS_FINE_LOCATION permission is not present */ @RequiresPermission(ACCESS_FINE_LOCATION) @@ -1893,6 +1917,9 @@ public class LocationManager { * @param listener a {@link OnNmeaMessageListener} object to register * @param executor the {@link Executor} that the listener runs on. * @return true if the listener was successfully added + * + * @throws IllegalArgumentException if executor is null + * @throws IllegalArgumentException if listener is null * @throws SecurityException if the ACCESS_FINE_LOCATION permission is not present */ @RequiresPermission(ACCESS_FINE_LOCATION) @@ -1965,6 +1992,9 @@ public class LocationManager { * @param callback a {@link GnssMeasurementsEvent.Callback} object to register. * @param handler the handler that the callback runs on. * @return {@code true} if the callback was added successfully, {@code false} otherwise. + * + * @throws IllegalArgumentException if callback is null + * @throws SecurityException if the ACCESS_FINE_LOCATION permission is not present */ @RequiresPermission(ACCESS_FINE_LOCATION) public boolean registerGnssMeasurementsCallback( @@ -1985,6 +2015,10 @@ public class LocationManager { * @param callback a {@link GnssMeasurementsEvent.Callback} object to register. * @param executor the executor that the callback runs on. * @return {@code true} if the callback was added successfully, {@code false} otherwise. + * + * @throws IllegalArgumentException if executor is null + * @throws IllegalArgumentException if callback is null + * @throws SecurityException if the ACCESS_FINE_LOCATION permission is not present */ @RequiresPermission(ACCESS_FINE_LOCATION) public boolean registerGnssMeasurementsCallback( @@ -2002,6 +2036,9 @@ public class LocationManager { * * @param measurementCorrections a {@link GnssMeasurementCorrections} object with the GNSS * measurement corrections to be injected into the GNSS chipset. + * + * @throws IllegalArgumentException if measurementCorrections is null + * @throws SecurityException if the ACCESS_FINE_LOCATION permission is not present * @hide */ @SystemApi @@ -2076,6 +2113,9 @@ public class LocationManager { * @param callback a {@link GnssNavigationMessage.Callback} object to register. * @param handler the handler that the callback runs on. * @return {@code true} if the callback was added successfully, {@code false} otherwise. + * + * @throws IllegalArgumentException if callback is null + * @throws SecurityException if the ACCESS_FINE_LOCATION permission is not present */ @RequiresPermission(ACCESS_FINE_LOCATION) public boolean registerGnssNavigationMessageCallback( @@ -2097,6 +2137,10 @@ public class LocationManager { * @param callback a {@link GnssNavigationMessage.Callback} object to register. * @param executor the looper that the callback runs on. * @return {@code true} if the callback was added successfully, {@code false} otherwise. + * + * @throws IllegalArgumentException if executor is null + * @throws IllegalArgumentException if callback is null + * @throws SecurityException if the ACCESS_FINE_LOCATION permission is not present */ @RequiresPermission(ACCESS_FINE_LOCATION) public boolean registerGnssNavigationMessageCallback( @@ -2557,7 +2601,7 @@ public class LocationManager { mListenerTransport = new GnssMeasurementsListener(); return mService.addGnssMeasurementsListener(mListenerTransport, - mContext.getPackageName()); + mContext.getPackageName(), "gnss measurement callback"); } @Override @@ -2593,7 +2637,7 @@ public class LocationManager { mListenerTransport = new GnssNavigationMessageListener(); return mService.addGnssNavigationMessageListener(mListenerTransport, - mContext.getPackageName()); + mContext.getPackageName(), "gnss navigation callback"); } @Override @@ -2628,7 +2672,8 @@ public class LocationManager { Preconditions.checkState(mListenerTransport == null); mListenerTransport = new BatchedLocationCallback(); - return mService.addGnssBatchingCallback(mListenerTransport, mContext.getPackageName()); + return mService.addGnssBatchingCallback(mListenerTransport, mContext.getPackageName(), + "batched location callback"); } @Override diff --git a/media/java/android/media/IMediaRoute2Provider.aidl b/media/java/android/media/IMediaRoute2Provider.aidl index f132cefbfdc7..66764c73ee5c 100644 --- a/media/java/android/media/IMediaRoute2Provider.aidl +++ b/media/java/android/media/IMediaRoute2Provider.aidl @@ -27,4 +27,6 @@ oneway interface IMediaRoute2Provider { void selectRoute(String packageName, String id); void unselectRoute(String packageName, String id); void notifyControlRequestSent(String id, in Intent request); + void requestSetVolume(String id, int volume); + void requestUpdateVolume(String id, int delta); } diff --git a/media/java/android/media/IMediaRouterService.aidl b/media/java/android/media/IMediaRouterService.aidl index 81213b943c81..7b7a34e5151f 100644 --- a/media/java/android/media/IMediaRouterService.aidl +++ b/media/java/android/media/IMediaRouterService.aidl @@ -46,6 +46,8 @@ interface IMediaRouterService { void registerClient2(IMediaRouter2Client client, String packageName); void unregisterClient2(IMediaRouter2Client client); void sendControlRequest(IMediaRouter2Client client, in MediaRoute2Info route, in Intent request); + void requestSetVolume2(IMediaRouter2Client client, in MediaRoute2Info route, int volume); + void requestUpdateVolume2(IMediaRouter2Client client, in MediaRoute2Info route, int direction); /** * Changes the selected route of the client. * @@ -66,4 +68,9 @@ interface IMediaRouterService { */ void selectClientRoute2(IMediaRouter2Manager manager, String packageName, in @nullable MediaRoute2Info route); + + void requestSetVolume2Manager(IMediaRouter2Manager manager, + in MediaRoute2Info route, int volume); + void requestUpdateVolume2Manager(IMediaRouter2Manager manager, + in MediaRoute2Info route, int direction); } diff --git a/media/java/android/media/MediaCodecInfo.java b/media/java/android/media/MediaCodecInfo.java index 26e79364bb02..91d644b5db74 100644 --- a/media/java/android/media/MediaCodecInfo.java +++ b/media/java/android/media/MediaCodecInfo.java @@ -21,6 +21,7 @@ import static android.media.Utils.sortDistinctRanges; import android.annotation.NonNull; import android.annotation.Nullable; +import android.annotation.SuppressLint; import android.annotation.TestApi; import android.annotation.UnsupportedAppUsage; import android.os.Build; @@ -3750,8 +3751,11 @@ public final class MediaCodecInfo { public static final int DolbyVisionProfileDvheStn = 0x20; public static final int DolbyVisionProfileDvheDth = 0x40; public static final int DolbyVisionProfileDvheDtb = 0x80; - public static final int DolbyVisionProfileDvheSt = 0x100; - public static final int DolbyVisionProfileDvavSe = 0x200; + public static final int DolbyVisionProfileDvheSt = 0x100; + public static final int DolbyVisionProfileDvavSe = 0x200; + /** Dolby Vision AV1 profile */ + @SuppressLint("AllUpper") + public static final int DolbyVisionProfileDvav110 = 0x400; public static final int DolbyVisionLevelHd24 = 0x1; public static final int DolbyVisionLevelHd30 = 0x2; diff --git a/media/java/android/media/MediaRoute2ProviderService.java b/media/java/android/media/MediaRoute2ProviderService.java index e8e0f826e6b6..58deff23b66a 100644 --- a/media/java/android/media/MediaRoute2ProviderService.java +++ b/media/java/android/media/MediaRoute2ProviderService.java @@ -81,6 +81,20 @@ public abstract class MediaRoute2ProviderService extends Service { public abstract void onControlRequest(String routeId, Intent request); /** + * Called when requestSetVolume is called on a route of the provider + * @param routeId the id of the route + * @param volume the target volume + */ + public abstract void onSetVolume(String routeId, int volume); + + /** + * Called when requestUpdateVolume is called on a route of the provider + * @param routeId id of the route + * @param delta the delta to add to the current volume + */ + public abstract void onUpdateVolume(String routeId, int delta); + + /** * Updates provider info and publishes routes */ public final void setProviderInfo(MediaRoute2ProviderInfo info) { @@ -130,5 +144,17 @@ public abstract class MediaRoute2ProviderService extends Service { mHandler.sendMessage(obtainMessage(MediaRoute2ProviderService::onControlRequest, MediaRoute2ProviderService.this, id, request)); } + + @Override + public void requestSetVolume(String id, int volume) { + mHandler.sendMessage(obtainMessage(MediaRoute2ProviderService::onSetVolume, + MediaRoute2ProviderService.this, id, volume)); + } + + @Override + public void requestUpdateVolume(String id, int delta) { + mHandler.sendMessage(obtainMessage(MediaRoute2ProviderService::onUpdateVolume, + MediaRoute2ProviderService.this, id, delta)); + } } } diff --git a/media/java/android/media/MediaRouter2.java b/media/java/android/media/MediaRouter2.java index ed35ef6a7ac7..aca40d8ee891 100644 --- a/media/java/android/media/MediaRouter2.java +++ b/media/java/android/media/MediaRouter2.java @@ -265,6 +265,54 @@ public class MediaRouter2 { } } + /** + * Requests a volume change for the route asynchronously. + * <p> + * It may have no effect if the route is currently not selected. + * </p> + * + * @param volume The new volume value between 0 and {@link MediaRoute2Info#getVolumeMax}. + */ + public void requestSetVolume(@NonNull MediaRoute2Info route, int volume) { + Objects.requireNonNull(route, "route must not be null"); + + Client client; + synchronized (sLock) { + client = mClient; + } + if (client != null) { + try { + mMediaRouterService.requestSetVolume2(client, route, volume); + } catch (RemoteException ex) { + Log.e(TAG, "Unable to send control request.", ex); + } + } + } + + /** + * Requests an incremental volume update for the route asynchronously. + * <p> + * It may have no effect if the route is currently not selected. + * </p> + * + * @param delta The delta to add to the current volume. + */ + public void requestUpdateVolume(@NonNull MediaRoute2Info route, int delta) { + Objects.requireNonNull(route, "route must not be null"); + + Client client; + synchronized (sLock) { + client = mClient; + } + if (client != null) { + try { + mMediaRouterService.requestUpdateVolume2(client, route, delta); + } catch (RemoteException ex) { + Log.e(TAG, "Unable to send control request.", ex); + } + } + } + @GuardedBy("mCallbackRecords") private int findCallbackRecordIndexLocked(Callback callback) { final int count = mCallbackRecords.size(); @@ -310,6 +358,7 @@ public class MediaRouter2 { List<MediaRoute2Info> outRoutes) { if (provider == null || !provider.isValid()) { Log.w(TAG, "Ignoring invalid provider : " + provider); + return; } final Collection<MediaRoute2Info> routes = provider.getRoutes(); @@ -321,10 +370,21 @@ public class MediaRouter2 { if (!route.supportsControlCategory(controlCategories)) { continue; } + MediaRoute2Info preRoute = findRouteById(route.getId()); + if (!route.equals(preRoute)) { + notifyRouteChanged(route); + } outRoutes.add(route); } } + MediaRoute2Info findRouteById(String id) { + for (MediaRoute2Info route : mRoutes) { + if (route.getId().equals(id)) return route; + } + return null; + } + void notifyRouteListChanged(List<MediaRoute2Info> routes) { for (CallbackRecord record: mCallbackRecords) { record.mExecutor.execute( @@ -332,10 +392,18 @@ public class MediaRouter2 { } } + void notifyRouteChanged(MediaRoute2Info route) { + for (CallbackRecord record: mCallbackRecords) { + record.mExecutor.execute( + () -> record.mCallback.onRouteChanged(route)); + } + } + /** * Interface for receiving events about media routing changes. */ public static class Callback { + //TODO: clean up these callbacks /** * Called when a route is added. */ @@ -369,7 +437,7 @@ public class MediaRouter2 { void notifyRoutes() { final List<MediaRoute2Info> routes = mRoutes; // notify only when bound to media router service. - //TODO: Correct the condition when control category, default rotue, .. are finalized. + //TODO: Correct the condition when control category, default route, .. are finalized. if (routes.size() > 0) { mExecutor.execute(() -> mCallback.onRoutesChanged(routes)); } diff --git a/media/java/android/media/MediaRouter2Manager.java b/media/java/android/media/MediaRouter2Manager.java index 0b645691ea3b..4f2a295b0779 100644 --- a/media/java/android/media/MediaRouter2Manager.java +++ b/media/java/android/media/MediaRouter2Manager.java @@ -234,6 +234,54 @@ public class MediaRouter2Manager { } } + /** + * Requests a volume change for the route asynchronously. + * <p> + * It may have no effect if the route is currently not selected. + * </p> + * + * @param volume The new volume value between 0 and {@link MediaRoute2Info#getVolumeMax}. + */ + public void requestSetVolume(@NonNull MediaRoute2Info route, int volume) { + Objects.requireNonNull(route, "route must not be null"); + + Client client; + synchronized (sLock) { + client = mClient; + } + if (client != null) { + try { + mMediaRouterService.requestSetVolume2Manager(client, route, volume); + } catch (RemoteException ex) { + Log.e(TAG, "Unable to send control request.", ex); + } + } + } + + /** + * Requests an incremental volume update for the route asynchronously. + * <p> + * It may have no effect if the route is currently not selected. + * </p> + * + * @param delta The delta to add to the current volume. + */ + public void requestUpdateVolume(@NonNull MediaRoute2Info route, int delta) { + Objects.requireNonNull(route, "route must not be null"); + + Client client; + synchronized (sLock) { + client = mClient; + } + if (client != null) { + try { + mMediaRouterService.requestUpdateVolume2Manager(client, route, delta); + } catch (RemoteException ex) { + Log.e(TAG, "Unable to send control request.", ex); + } + } + } + int findProviderIndex(MediaRoute2ProviderInfo provider) { final int count = mProviders.size(); for (int i = 0; i < count; i++) { diff --git a/media/tests/MediaRouteProvider/src/com/android/mediarouteprovider/example/SampleMediaRoute2ProviderService.java b/media/tests/MediaRouteProvider/src/com/android/mediarouteprovider/example/SampleMediaRoute2ProviderService.java index 8d39a930a8f2..680c87930676 100644 --- a/media/tests/MediaRouteProvider/src/com/android/mediarouteprovider/example/SampleMediaRoute2ProviderService.java +++ b/media/tests/MediaRouteProvider/src/com/android/mediarouteprovider/example/SampleMediaRoute2ProviderService.java @@ -130,6 +130,33 @@ public class SampleMediaRoute2ProviderService extends MediaRoute2ProviderService } } + @Override + public void onSetVolume(String routeId, int volume) { + MediaRoute2Info route = mRoutes.get(routeId); + if (route == null) { + return; + } + volume = Math.min(volume, Math.max(0, route.getVolumeMax())); + mRoutes.put(routeId, new MediaRoute2Info.Builder(route) + .setVolume(volume) + .build()); + publishRoutes(); + } + + @Override + public void onUpdateVolume(String routeId, int delta) { + MediaRoute2Info route = mRoutes.get(routeId); + if (route == null) { + return; + } + int volume = route.getVolume() + delta; + volume = Math.min(volume, Math.max(0, route.getVolumeMax())); + mRoutes.put(routeId, new MediaRoute2Info.Builder(route) + .setVolume(volume) + .build()); + publishRoutes(); + } + void publishRoutes() { MediaRoute2ProviderInfo info = new MediaRoute2ProviderInfo.Builder() .addRoutes(mRoutes.values()) diff --git a/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouterManagerTest.java b/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouterManagerTest.java index da832ac5241d..ca43d04573f3 100644 --- a/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouterManagerTest.java +++ b/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouterManagerTest.java @@ -47,8 +47,10 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.concurrent.CountDownLatch; import java.util.concurrent.Executor; import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; @RunWith(AndroidJUnit4.class) @SmallTest @@ -190,17 +192,8 @@ public class MediaRouterManagerTest { MediaRouter2Manager.Callback mockCallback = mock(MediaRouter2Manager.Callback.class); mManager.registerCallback(mExecutor, mockCallback); - MediaRouter2.Callback mockRouterCallback = mock(MediaRouter2.Callback.class); - - mRouter2.setControlCategories(CONTROL_CATEGORIES_SPECIAL); - mRouter2.registerCallback(mExecutor, mockRouterCallback); - mRouter2.unregisterCallback(mockRouterCallback); - - verify(mockCallback, timeout(TIMEOUT_MS)) - .onRoutesChanged(argThat(routes -> routes.size() > 0)); - Map<String, MediaRoute2Info> routes = - createRouteMap(mManager.getAvailableRoutes(mPackageName)); + waitAndGetRoutesWithManager(CONTROL_CATEGORIES_SPECIAL); Assert.assertEquals(1, routes.size()); Assert.assertNotNull(routes.get(ROUTE_ID_SPECIAL_CATEGORY)); @@ -214,12 +207,10 @@ public class MediaRouterManagerTest { @Test public void testGetRoutes() throws Exception { MediaRouter2.Callback mockCallback = mock(MediaRouter2.Callback.class); - - mRouter2.setControlCategories(CONTROL_CATEGORIES_SPECIAL); mRouter2.registerCallback(mExecutor, mockCallback); - verify(mockCallback, timeout(TIMEOUT_MS).atLeastOnce()) - .onRoutesChanged(argThat(routes -> routes.size() > 0)); - Map<String, MediaRoute2Info> routes = createRouteMap(mRouter2.getRoutes()); + + Map<String, MediaRoute2Info> routes = waitAndGetRoutes(CONTROL_CATEGORIES_SPECIAL); + Assert.assertEquals(1, routes.size()); Assert.assertNotNull(routes.get(ROUTE_ID_SPECIAL_CATEGORY)); @@ -228,51 +219,40 @@ public class MediaRouterManagerTest { @Test public void testOnRouteSelected() throws Exception { - MediaRouter2.Callback mockRouterCallback = mock(MediaRouter2.Callback.class); + MediaRouter2.Callback routerCallback = new MediaRouter2.Callback(); MediaRouter2Manager.Callback managerCallback = mock(MediaRouter2Manager.Callback.class); mManager.registerCallback(mExecutor, managerCallback); - mRouter2.setControlCategories(CONTROL_CATEGORIES_ALL); - mRouter2.registerCallback(mExecutor, mockRouterCallback); - - verify(managerCallback, timeout(TIMEOUT_MS)) - .onRoutesChanged(argThat(routes -> routes.size() > 0)); + mRouter2.registerCallback(mExecutor, routerCallback); - Map<String, MediaRoute2Info> routes = - createRouteMap(mManager.getAvailableRoutes(mPackageName)); + Map<String, MediaRoute2Info> routes = waitAndGetRoutesWithManager(CONTROL_CATEGORIES_ALL); MediaRoute2Info routeToSelect = routes.get(ROUTE_ID1); - mManager.selectRoute(mPackageName, routeToSelect); - assertNotNull(routeToSelect); + + mManager.selectRoute(mPackageName, routeToSelect); verify(managerCallback, timeout(TIMEOUT_MS)) .onRouteAdded(argThat(route -> route.equals(routeToSelect))); + mRouter2.unregisterCallback(routerCallback); mManager.unregisterCallback(managerCallback); - mRouter2.unregisterCallback(mockRouterCallback); } /** * Tests selecting and unselecting routes of a single provider. */ @Test - public void testSingleProviderSelect() { + public void testSingleProviderSelect() throws Exception { MediaRouter2Manager.Callback managerCallback = mock(MediaRouter2Manager.Callback.class); MediaRouter2.Callback routerCallback = mock(MediaRouter2.Callback.class); mManager.registerCallback(mExecutor, managerCallback); - mRouter2.setControlCategories(CONTROL_CATEGORIES_ALL); mRouter2.registerCallback(mExecutor, routerCallback); - verify(managerCallback, timeout(TIMEOUT_MS)) - .onRoutesChanged(argThat(routes -> routes.size() > 0)); - - Map<String, MediaRoute2Info> routes = - createRouteMap(mManager.getAvailableRoutes(mPackageName)); + Map<String, MediaRoute2Info> routes = waitAndGetRoutesWithManager(CONTROL_CATEGORIES_ALL); mManager.selectRoute(mPackageName, routes.get(ROUTE_ID1)); - verify(managerCallback, timeout(TIMEOUT_MS) - ) + verify(managerCallback, timeout(TIMEOUT_MS)) .onRouteChanged(argThat(routeInfo -> TextUtils.equals(ROUTE_ID1, routeInfo.getId()) && TextUtils.equals(routeInfo.getClientPackageName(), mPackageName))); @@ -291,14 +271,67 @@ public class MediaRouterManagerTest { } @Test - public void testVolumeHandling() { + public void testControlVolumeWithRouter() throws Exception { MediaRouter2.Callback mockCallback = mock(MediaRouter2.Callback.class); - mRouter2.setControlCategories(CONTROL_CATEGORIES_ALL); + Map<String, MediaRoute2Info> routes = waitAndGetRoutes(CONTROL_CATEGORIES_ALL); mRouter2.registerCallback(mExecutor, mockCallback); + + MediaRoute2Info volRoute = routes.get(ROUTE_ID_VARIABLE_VOLUME); + int originalVolume = volRoute.getVolume(); + int deltaVolume = (originalVolume == volRoute.getVolumeMax() ? -1 : 1); + int targetVolume = originalVolume + deltaVolume; + + mRouter2.requestSetVolume(volRoute, targetVolume); verify(mockCallback, timeout(TIMEOUT_MS).atLeastOnce()) - .onRoutesChanged(argThat(routes -> routes.size() > 0)); - Map<String, MediaRoute2Info> routes = createRouteMap(mRouter2.getRoutes()); + .onRouteChanged(argThat(route -> + route.getId().equals(volRoute.getId()) + && route.getVolume() == targetVolume)); + + mRouter2.requestUpdateVolume(volRoute, -deltaVolume); + verify(mockCallback, timeout(TIMEOUT_MS).atLeastOnce()) + .onRouteChanged(argThat(route -> + route.getId().equals(volRoute.getId()) + && route.getVolume() == originalVolume)); + + mRouter2.unregisterCallback(mockCallback); + } + + @Test + public void testControlVolumeWithManager() throws Exception { + MediaRouter2Manager.Callback managerCallback = mock(MediaRouter2Manager.Callback.class); + MediaRouter2.Callback mockCallback = mock(MediaRouter2.Callback.class); + + mManager.registerCallback(mExecutor, managerCallback); + mRouter2.registerCallback(mExecutor, mockCallback); + Map<String, MediaRoute2Info> routes = waitAndGetRoutesWithManager(CONTROL_CATEGORIES_ALL); + + MediaRoute2Info volRoute = routes.get(ROUTE_ID_VARIABLE_VOLUME); + int originalVolume = volRoute.getVolume(); + int deltaVolume = (originalVolume == volRoute.getVolumeMax() ? -1 : 1); + int targetVolume = originalVolume + deltaVolume; + + mManager.requestSetVolume(volRoute, targetVolume); + verify(mockCallback, timeout(TIMEOUT_MS).atLeastOnce()) + .onRouteChanged(argThat(route -> + route.getId().equals(volRoute.getId()) + && route.getVolume() == targetVolume)); + + mManager.requestUpdateVolume(volRoute, -deltaVolume); + verify(mockCallback, timeout(TIMEOUT_MS).atLeastOnce()) + .onRouteChanged(argThat(route -> + route.getId().equals(volRoute.getId()) + && route.getVolume() == originalVolume)); + + mRouter2.unregisterCallback(mockCallback); + mManager.unregisterCallback(managerCallback); + } + + @Test + public void testVolumeHandling() throws Exception { + MediaRouter2.Callback mockCallback = mock(MediaRouter2.Callback.class); + mRouter2.registerCallback(mExecutor, mockCallback); + Map<String, MediaRoute2Info> routes = waitAndGetRoutes(CONTROL_CATEGORIES_ALL); MediaRoute2Info fixedVolumeRoute = routes.get(ROUTE_ID_FIXED_VOLUME); MediaRoute2Info variableVolumeRoute = routes.get(ROUTE_ID_VARIABLE_VOLUME); @@ -310,6 +343,48 @@ public class MediaRouterManagerTest { mRouter2.unregisterCallback(mockCallback); } + Map<String, MediaRoute2Info> waitAndGetRoutes(List<String> controlCategories) throws Exception { + CountDownLatch latch = new CountDownLatch(1); + MediaRouter2.Callback callback = new MediaRouter2.Callback() { + @Override + public void onRoutesChanged(List<MediaRoute2Info> routes) { + if (routes.size() > 0) latch.countDown(); + } + }; + mRouter2.setControlCategories(controlCategories); + mRouter2.registerCallback(mExecutor, callback); + try { + latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS); + return createRouteMap(mRouter2.getRoutes()); + } finally { + mRouter2.unregisterCallback(callback); + } + } + + Map<String, MediaRoute2Info> waitAndGetRoutesWithManager(List<String> controlCategories) + throws Exception { + CountDownLatch latch = new CountDownLatch(1); + + // Dummy callback is required to send control category info. + MediaRouter2.Callback routerCallback = new MediaRouter2.Callback(); + MediaRouter2Manager.Callback managerCallback = new MediaRouter2Manager.Callback() { + @Override + public void onRoutesChanged(List<MediaRoute2Info> routes) { + if (routes.size() > 0) latch.countDown(); + } + }; + mManager.registerCallback(mExecutor, managerCallback); + mRouter2.setControlCategories(controlCategories); + mRouter2.registerCallback(mExecutor, routerCallback); + try { + latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS); + return createRouteMap(mManager.getAvailableRoutes(mPackageName)); + } finally { + mRouter2.unregisterCallback(routerCallback); + mManager.unregisterCallback(managerCallback); + } + } + // Helper for getting routes easily static Map<String, MediaRoute2Info> createRouteMap(List<MediaRoute2Info> routes) { Map<String, MediaRoute2Info> routeMap = new HashMap<>(); diff --git a/packages/CarSystemUI/res/layout/car_top_navigation_bar_unprovisioned.xml b/packages/CarSystemUI/res/layout/car_top_navigation_bar_unprovisioned.xml new file mode 100644 index 000000000000..8247211dcb32 --- /dev/null +++ b/packages/CarSystemUI/res/layout/car_top_navigation_bar_unprovisioned.xml @@ -0,0 +1,147 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2019 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License + --> + +<com.android.systemui.statusbar.car.CarNavigationBarView + xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:systemui="http://schemas.android.com/apk/res-auto" + android:id="@+id/car_top_bar" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:background="@drawable/system_bar_background" + android:orientation="vertical"> + + <RelativeLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_weight="1"> + + <FrameLayout + android:id="@+id/left_hvac_container" + android:layout_width="wrap_content" + android:layout_height="match_parent" + android:layout_alignParentStart="true" + > + + <com.android.systemui.statusbar.car.CarNavigationButton + android:id="@+id/hvacleft" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:background="@null" + systemui:broadcast="true" + systemui:intent="intent:#Intent;action=android.car.intent.action.TOGGLE_HVAC_CONTROLS;end" + /> + + <com.android.systemui.statusbar.hvac.AnimatedTemperatureView + android:id="@+id/lefttext" + android:layout_width="wrap_content" + android:layout_height="match_parent" + android:paddingStart="@*android:dimen/car_padding_4" + android:paddingEnd="16dp" + android:gravity="center_vertical|start" + android:minEms="4" + android:textAppearance="@style/TextAppearance.CarStatus" + systemui:hvacAreaId="49" + systemui:hvacMaxText="@string/hvac_max_text" + systemui:hvacMaxValue="@dimen/hvac_max_value" + systemui:hvacMinText="@string/hvac_min_text" + systemui:hvacMinValue="@dimen/hvac_min_value" + systemui:hvacPivotOffset="60dp" + systemui:hvacPropertyId="358614275" + systemui:hvacTempFormat="%.0f\u00B0" + /> + </FrameLayout> + + <FrameLayout + android:id="@+id/clock_container" + android:layout_width="wrap_content" + android:layout_height="match_parent" + android:layout_centerInParent="true"> + <com.android.systemui.statusbar.car.CarNavigationButton + android:id="@+id/qs" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:background="@null"/> + <com.android.systemui.statusbar.policy.Clock + android:id="@+id/clock" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_gravity="center" + android:elevation="5dp" + android:singleLine="true" + android:textAppearance="@style/TextAppearance.StatusBar.Clock"/> + </FrameLayout> + + <LinearLayout + android:id="@+id/system_icon_area" + android:layout_width="wrap_content" + android:layout_height="match_parent" + android:layout_centerHorizontal="true" + android:layout_centerVertical="true" + android:layout_toEndOf="@+id/clock_container" + android:paddingStart="@*android:dimen/car_padding_1" + android:gravity="center_vertical" + android:orientation="horizontal" + > + + <include + layout="@layout/system_icons" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_weight="1" + android:paddingStart="4dp" + android:gravity="center_vertical" + /> + </LinearLayout> + + <FrameLayout + android:id="@+id/right_hvac_container" + android:layout_width="wrap_content" + android:layout_height="match_parent" + android:layout_alignParentEnd="true" + > + + <com.android.systemui.statusbar.car.CarNavigationButton + android:id="@+id/hvacright" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:background="@null" + systemui:broadcast="true" + systemui:intent="intent:#Intent;action=android.car.intent.action.TOGGLE_HVAC_CONTROLS;end" + /> + + <com.android.systemui.statusbar.hvac.AnimatedTemperatureView + android:id="@+id/righttext" + android:layout_width="wrap_content" + android:layout_height="match_parent" + android:paddingStart="16dp" + android:paddingEnd="@*android:dimen/car_padding_4" + android:gravity="center_vertical|end" + android:minEms="4" + android:textAppearance="@style/TextAppearance.CarStatus" + systemui:hvacAreaId="68" + systemui:hvacMaxText="@string/hvac_max_text" + systemui:hvacMaxValue="@dimen/hvac_max_value" + systemui:hvacMinText="@string/hvac_min_text" + systemui:hvacMinValue="@dimen/hvac_min_value" + systemui:hvacPivotOffset="60dp" + systemui:hvacPropertyId="358614275" + systemui:hvacTempFormat="%.0f\u00B0" + /> + </FrameLayout> + </RelativeLayout> + +</com.android.systemui.statusbar.car.CarNavigationBarView> diff --git a/packages/CarSystemUI/res/layout/super_status_bar.xml b/packages/CarSystemUI/res/layout/super_status_bar.xml index 08d48bfb1230..37cd1d49cb85 100644 --- a/packages/CarSystemUI/res/layout/super_status_bar.xml +++ b/packages/CarSystemUI/res/layout/super_status_bar.xml @@ -69,10 +69,10 @@ android:visibility="gone" /> - <include layout="@layout/car_top_navigation_bar" + <FrameLayout + android:id="@+id/car_top_navigation_bar_container" android:layout_width="match_parent" - android:layout_height="wrap_content" - /> + android:layout_height="wrap_content"/> </LinearLayout> <include layout="@layout/brightness_mirror"/> diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java index a585727a25e9..9d47cdc6e3e6 100644 --- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java +++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java @@ -162,9 +162,11 @@ public class CarStatusBar extends StatusBar implements CarBatteryController.Batt private BatteryMeterView mBatteryMeterView; private Drawable mNotificationPanelBackground; + private ViewGroup mTopNavigationBarContainer; private ViewGroup mNavigationBarWindow; private ViewGroup mLeftNavigationBarWindow; private ViewGroup mRightNavigationBarWindow; + private CarNavigationBarView mTopNavigationBarView; private CarNavigationBarView mNavigationBarView; private CarNavigationBarView mLeftNavigationBarView; private CarNavigationBarView mRightNavigationBarView; @@ -176,7 +178,7 @@ public class CarStatusBar extends StatusBar implements CarBatteryController.Batt private CarFacetButtonController mCarFacetButtonController; private ActivityManagerWrapper mActivityManagerWrapper; private DeviceProvisionedController mDeviceProvisionedController; - private boolean mDeviceIsProvisioned = true; + private boolean mDeviceIsSetUpForUser = true; private HvacController mHvacController; private DrivingStateHelper mDrivingStateHelper; private PowerManagerHelper mPowerManagerHelper; @@ -197,6 +199,9 @@ public class CarStatusBar extends StatusBar implements CarBatteryController.Batt private boolean mNotificationListAtBottom; // Was the notification list at the bottom when the user first touched the screen private boolean mNotificationListAtBottomAtTimeOfTouch; + // To be attached to the top navigation bar (i.e. status bar) to pull down the notification + // panel. + private View.OnTouchListener mTopNavBarNotificationTouchListener; // To be attached to the navigation bars such that they can close the notification panel if // it's open. private View.OnTouchListener mNavBarNotificationTouchListener; @@ -364,7 +369,7 @@ public class CarStatusBar extends StatusBar implements CarBatteryController.Batt // get the provisioned state before calling the parent class since it's that flow that // builds the nav bar mDeviceProvisionedController = Dependency.get(DeviceProvisionedController.class); - mDeviceIsProvisioned = mDeviceProvisionedController.isDeviceProvisioned(); + mDeviceIsSetUpForUser = mDeviceProvisionedController.isCurrentUserSetup(); // Keyboard related setup, before nav bars are created. mHideNavBarForKeyboard = mContext.getResources().getBoolean( @@ -376,6 +381,10 @@ public class CarStatusBar extends StatusBar implements CarBatteryController.Batt mScreenLifecycle = Dependency.get(ScreenLifecycle.class); mScreenLifecycle.addObserver(mScreenObserver); + // Need to initialize HVAC controller before calling super.start - before system bars are + // created. + mHvacController = new HvacController(mContext); + super.start(); mTaskStackListener = new TaskStackListenerImpl(); mActivityManagerWrapper = ActivityManagerWrapper.getInstance(); @@ -394,25 +403,18 @@ public class CarStatusBar extends StatusBar implements CarBatteryController.Batt mHvacController.connectToCarService(); - CarSystemUIFactory factory = SystemUIFactory.getInstance(); - if (!mDeviceIsProvisioned) { - mDeviceProvisionedController.addCallback( - new DeviceProvisionedController.DeviceProvisionedListener() { - @Override - public void onDeviceProvisionedChanged() { - mHandler.post(() -> { - // on initial boot we are getting a call even though the value - // is the same so we are confirming the reset is needed - boolean deviceProvisioned = - mDeviceProvisionedController.isDeviceProvisioned(); - if (mDeviceIsProvisioned != deviceProvisioned) { - mDeviceIsProvisioned = deviceProvisioned; - restartNavBars(); - } - }); - } - }); - } + mDeviceProvisionedController.addCallback( + new DeviceProvisionedController.DeviceProvisionedListener() { + @Override + public void onUserSetupChanged() { + mHandler.post(() -> restartNavBarsIfNecessary()); + } + + @Override + public void onUserSwitched() { + mHandler.post(() -> restartNavBarsIfNecessary()); + } + }); // Register a listener for driving state changes. mDrivingStateHelper = new DrivingStateHelper(mContext, this::onDrivingStateChanged); @@ -424,6 +426,14 @@ public class CarStatusBar extends StatusBar implements CarBatteryController.Batt mSwitchToGuestTimer = new SwitchToGuestTimer(mContext); } + private void restartNavBarsIfNecessary() { + boolean currentUserSetup = mDeviceProvisionedController.isCurrentUserSetup(); + if (mDeviceIsSetUpForUser != currentUserSetup) { + mDeviceIsSetUpForUser = currentUserSetup; + restartNavBars(); + } + } + /** * Remove all content from navbars and rebuild them. Used to allow for different nav bars * before and after the device is provisioned. . Also for change of density and font size. @@ -432,8 +442,8 @@ public class CarStatusBar extends StatusBar implements CarBatteryController.Batt // remove and reattach all hvac components such that we don't keep a reference to unused // ui elements mHvacController.removeAllComponents(); - addTemperatureViewToController(mStatusBarWindow); mCarFacetButtonController.removeAll(); + if (mNavigationBarWindow != null) { mNavigationBarWindow.removeAllViews(); mNavigationBarView = null; @@ -527,7 +537,6 @@ public class CarStatusBar extends StatusBar implements CarBatteryController.Batt @Override protected void makeStatusBarView(@Nullable RegisterStatusBarResult result) { super.makeStatusBarView(result); - mHvacController = new HvacController(mContext); CarSystemUIFactory factory = SystemUIFactory.getInstance(); mCarFacetButtonController = factory.getCarDependencyComponent() @@ -552,7 +561,8 @@ public class CarStatusBar extends StatusBar implements CarBatteryController.Batt * touch listeners needed for opening and closing the notification panel */ private void connectNotificationsUI() { - // Attached to the status bar to detect pull down of the notification shade. + // Attached to the top navigation bar (i.e. status bar) to detect pull down of the + // notification shade. GestureDetector openGestureDetector = new GestureDetector(mContext, new OpenNotificationGestureListener() { @Override @@ -585,6 +595,18 @@ public class CarStatusBar extends StatusBar implements CarBatteryController.Batt GestureDetector handleBarCloseNotificationGestureDetector = new GestureDetector(mContext, new HandleBarCloseNotificationGestureListener()); + mTopNavBarNotificationTouchListener = (v, event) -> { + if (!mDeviceIsSetUpForUser) { + return true; + } + boolean consumed = openGestureDetector.onTouchEvent(event); + if (consumed) { + return true; + } + maybeCompleteAnimation(event); + return true; + }; + mNavBarNotificationTouchListener = (v, event) -> { boolean consumed = navBarCloseNotificationGestureDetector.onTouchEvent(event); @@ -595,21 +617,6 @@ public class CarStatusBar extends StatusBar implements CarBatteryController.Batt return true; }; - // The following are the ui elements that the user would call the status bar. - // This will set the status bar so it they can make call backs. - CarNavigationBarView topBar = mStatusBarWindow.findViewById(R.id.car_top_bar); - topBar.setStatusBar(this); - topBar.setStatusBarWindowTouchListener((v1, event1) -> { - - boolean consumed = openGestureDetector.onTouchEvent(event1); - if (consumed) { - return true; - } - maybeCompleteAnimation(event1); - return true; - } - ); - mNotificationClickHandlerFactory = new NotificationClickHandlerFactory(mBarService); mNotificationClickHandlerFactory.registerClickListener((launchResult, alertEntry) -> { if (launchResult == ActivityManager.START_TASK_TO_FRONT @@ -916,23 +923,30 @@ public class CarStatusBar extends StatusBar implements CarBatteryController.Batt } private void buildNavBarContent() { + // Always build top bar. + buildTopBar((mDeviceIsSetUpForUser) ? R.layout.car_top_navigation_bar : + R.layout.car_top_navigation_bar_unprovisioned); + if (mShowBottom) { - buildBottomBar((mDeviceIsProvisioned) ? R.layout.car_navigation_bar : + buildBottomBar((mDeviceIsSetUpForUser) ? R.layout.car_navigation_bar : R.layout.car_navigation_bar_unprovisioned); } if (mShowLeft) { - buildLeft((mDeviceIsProvisioned) ? R.layout.car_left_navigation_bar : + buildLeft((mDeviceIsSetUpForUser) ? R.layout.car_left_navigation_bar : R.layout.car_left_navigation_bar_unprovisioned); } if (mShowRight) { - buildRight((mDeviceIsProvisioned) ? R.layout.car_right_navigation_bar : + buildRight((mDeviceIsSetUpForUser) ? R.layout.car_right_navigation_bar : R.layout.car_right_navigation_bar_unprovisioned); } } private void buildNavBarWindows() { + mTopNavigationBarContainer = mStatusBarWindow + .findViewById(R.id.car_top_navigation_bar_container); + if (mShowBottom) { mNavigationBarWindow = (ViewGroup) View.inflate(mContext, R.layout.navigation_bar_window, null); @@ -965,15 +979,25 @@ public class CarStatusBar extends StatusBar implements CarBatteryController.Batt } boolean isKeyboardVisible = (vis & InputMethodService.IME_VISIBLE) != 0; - if (!isKeyboardVisible) { - attachBottomNavBarWindow(); - } else { - detachBottomNavBarWindow(); - } + showBottomNavBarWindow(isKeyboardVisible); } private void attachNavBarWindows() { - attachBottomNavBarWindow(); + if (mShowBottom && !mBottomNavBarVisible) { + mBottomNavBarVisible = true; + + WindowManager.LayoutParams lp = new WindowManager.LayoutParams( + LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT, + WindowManager.LayoutParams.TYPE_NAVIGATION_BAR, + WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE + | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL + | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH + | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH, + PixelFormat.TRANSLUCENT); + lp.setTitle("CarNavigationBar"); + lp.windowAnimations = 0; + mWindowManager.addView(mNavigationBarWindow, lp); + } if (mShowLeft) { int width = mContext.getResources().getDimensionPixelSize( @@ -1011,47 +1035,32 @@ public class CarStatusBar extends StatusBar implements CarBatteryController.Batt } } - /** - * Attaches the bottom nav bar window. Can be extended to modify the specific behavior of - * attaching the bottom nav bar. - */ - protected void attachBottomNavBarWindow() { + private void showBottomNavBarWindow(boolean isKeyboardVisible) { if (!mShowBottom) { return; } - if (mBottomNavBarVisible) { + // If keyboard is visible and bottom nav bar not visible, this is the correct state, so do + // nothing. Same with if keyboard is not visible and bottom nav bar is visible. + if (isKeyboardVisible ^ mBottomNavBarVisible) { return; } - mBottomNavBarVisible = true; - - WindowManager.LayoutParams lp = new WindowManager.LayoutParams( - LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT, - WindowManager.LayoutParams.TYPE_NAVIGATION_BAR, - WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE - | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL - | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH - | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH, - PixelFormat.TRANSLUCENT); - lp.setTitle("CarNavigationBar"); - lp.windowAnimations = 0; - mWindowManager.addView(mNavigationBarWindow, lp); - } - /** - * Detaches the bottom nav bar window. Can be extended to modify the specific behavior of - * detaching the bottom nav bar. - */ - protected void detachBottomNavBarWindow() { - if (!mShowBottom) { - return; - } + mNavigationBarWindow.setVisibility(isKeyboardVisible ? View.GONE : View.VISIBLE); + mBottomNavBarVisible = !isKeyboardVisible; + } - if (!mBottomNavBarVisible) { - return; - } - mBottomNavBarVisible = false; - mWindowManager.removeView(mNavigationBarWindow); + private void buildTopBar(int layout) { + mTopNavigationBarContainer.removeAllViews(); + View.inflate(mContext, layout, mTopNavigationBarContainer); + mTopNavigationBarView = (CarNavigationBarView) mTopNavigationBarContainer.getChildAt(0); + if (mTopNavigationBarView == null) { + Log.e(TAG, "CarStatusBar failed inflate for R.layout.car_top_navigation_bar"); + throw new RuntimeException("Unable to build top nav bar due to missing layout"); + } + mTopNavigationBarView.setStatusBar(this); + addTemperatureViewToController(mTopNavigationBarView); + mTopNavigationBarView.setStatusBarWindowTouchListener(mTopNavBarNotificationTouchListener); } private void buildBottomBar(int layout) { @@ -1062,7 +1071,7 @@ public class CarStatusBar extends StatusBar implements CarBatteryController.Batt mNavigationBarView = (CarNavigationBarView) mNavigationBarWindow.getChildAt(0); if (mNavigationBarView == null) { Log.e(TAG, "CarStatusBar failed inflate for R.layout.car_navigation_bar"); - throw new RuntimeException("Unable to build botom nav bar due to missing layout"); + throw new RuntimeException("Unable to build bottom nav bar due to missing layout"); } mNavigationBarView.setStatusBar(this); addTemperatureViewToController(mNavigationBarView); @@ -1073,7 +1082,7 @@ public class CarStatusBar extends StatusBar implements CarBatteryController.Batt View.inflate(mContext, layout, mLeftNavigationBarWindow); mLeftNavigationBarView = (CarNavigationBarView) mLeftNavigationBarWindow.getChildAt(0); if (mLeftNavigationBarView == null) { - Log.e(TAG, "CarStatusBar failed inflate for R.layout.car_navigation_bar"); + Log.e(TAG, "CarStatusBar failed inflate for R.layout.car_left_navigation_bar"); throw new RuntimeException("Unable to build left nav bar due to missing layout"); } mLeftNavigationBarView.setStatusBar(this); @@ -1086,7 +1095,7 @@ public class CarStatusBar extends StatusBar implements CarBatteryController.Batt View.inflate(mContext, layout, mRightNavigationBarWindow); mRightNavigationBarView = (CarNavigationBarView) mRightNavigationBarWindow.getChildAt(0); if (mRightNavigationBarView == null) { - Log.e(TAG, "CarStatusBar failed inflate for R.layout.car_navigation_bar"); + Log.e(TAG, "CarStatusBar failed inflate for R.layout.car_right_navigation_bar"); throw new RuntimeException("Unable to build right nav bar due to missing layout"); } mRightNavigationBarView.setStatusBar(this); diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/privacy/OngoingPrivacyChip.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/privacy/OngoingPrivacyChip.java index ead1de2bd352..88d641e8bb36 100644 --- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/privacy/OngoingPrivacyChip.java +++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/privacy/OngoingPrivacyChip.java @@ -47,6 +47,7 @@ import java.util.stream.Collectors; public class OngoingPrivacyChip extends LinearLayout implements View.OnClickListener { private Context mContext; + private Handler mHandler; private LinearLayout mIconsContainer; private List<PrivacyItem> mPrivacyItems; @@ -88,6 +89,7 @@ public class OngoingPrivacyChip extends LinearLayout implements View.OnClickList private void init(Context context) { mContext = context; + mHandler = new Handler(Looper.getMainLooper()); mPrivacyItems = new ArrayList<>(); sAppOpsController = Dependency.get(AppOpsController.class); mUserManager = mContext.getSystemService(UserManager.class); @@ -131,8 +133,7 @@ public class OngoingPrivacyChip extends LinearLayout implements View.OnClickList @Override public void onClick(View v) { updatePrivacyList(); - Handler mUiHandler = new Handler(Looper.getMainLooper()); - mUiHandler.post(() -> { + mHandler.post(() -> { mActivityStarter.postStartActivityDismissingKeyguard( new Intent(Intent.ACTION_REVIEW_ONGOING_PERMISSION_USAGE), 0); }); @@ -152,21 +153,17 @@ public class OngoingPrivacyChip extends LinearLayout implements View.OnClickList } private void updatePrivacyList() { - mPrivacyItems = mCurrentUserIds.stream() + List<PrivacyItem> privacyItems = mCurrentUserIds.stream() .flatMap(item -> sAppOpsController.getActiveAppOpsForUser(item).stream()) .filter(Objects::nonNull) .map(item -> toPrivacyItem(item)) .filter(Objects::nonNull) .collect(Collectors.toList()); - mPrivacyDialogBuilder = new PrivacyDialogBuilder(mContext, mPrivacyItems); - - Handler refresh = new Handler(Looper.getMainLooper()); - refresh.post(new Runnable() { - @Override - public void run() { - updateView(); - } - }); + if (!privacyItems.equals(mPrivacyItems)) { + mPrivacyItems = privacyItems; + mPrivacyDialogBuilder = new PrivacyDialogBuilder(mContext, mPrivacyItems); + mHandler.post(this::updateView); + } } private PrivacyItem toPrivacyItem(AppOpItem appOpItem) { diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/privacy/PrivacyApplication.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/privacy/PrivacyApplication.java index 5ec7a77cb305..820bea99823e 100644 --- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/privacy/PrivacyApplication.java +++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/privacy/PrivacyApplication.java @@ -23,16 +23,20 @@ import android.content.pm.PackageManager; import android.graphics.drawable.Drawable; import android.util.Log; +import java.util.Objects; + /** * Class to hold the data for the applications that are using the AppOps permissions. */ public class PrivacyApplication { private static final String TAG = "PrivacyApplication"; + private String mPackageName; private Drawable mIcon; private String mApplicationName; public PrivacyApplication(String packageName, Context context) { + mPackageName = packageName; try { CarUserManagerHelper carUserManagerHelper = new CarUserManagerHelper(context); ApplicationInfo app = context.getPackageManager() @@ -59,4 +63,17 @@ public class PrivacyApplication { public String getApplicationName() { return mApplicationName; } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + PrivacyApplication that = (PrivacyApplication) o; + return mPackageName.equals(that.mPackageName); + } + + @Override + public int hashCode() { + return Objects.hash(mPackageName); + } } diff --git a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/privacy/PrivacyItem.java b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/privacy/PrivacyItem.java index fca137392d74..d3e123ed8c25 100644 --- a/packages/CarSystemUI/src/com/android/systemui/statusbar/car/privacy/PrivacyItem.java +++ b/packages/CarSystemUI/src/com/android/systemui/statusbar/car/privacy/PrivacyItem.java @@ -16,6 +16,8 @@ package com.android.systemui.statusbar.car.privacy; +import java.util.Objects; + /** * Class for holding the data of each privacy item displayed in {@link OngoingPrivacyDialog} */ @@ -43,4 +45,18 @@ public class PrivacyItem { public PrivacyType getPrivacyType() { return mPrivacyType; } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + PrivacyItem that = (PrivacyItem) o; + return mPrivacyType == that.mPrivacyType + && mPrivacyApplication.equals(that.mPrivacyApplication); + } + + @Override + public int hashCode() { + return Objects.hash(mPrivacyType, mPrivacyApplication); + } } diff --git a/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CaptivePortalLoginActivity.java b/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CaptivePortalLoginActivity.java index 81c5bcd47981..642dc82c4d28 100644 --- a/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CaptivePortalLoginActivity.java +++ b/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CaptivePortalLoginActivity.java @@ -35,7 +35,6 @@ import android.net.Uri; import android.net.http.SslError; import android.os.Bundle; import android.telephony.CarrierConfigManager; -import android.telephony.Rlog; import android.telephony.SubscriptionManager; import android.text.TextUtils; import android.util.ArrayMap; @@ -477,11 +476,11 @@ public class CaptivePortalLoginActivity extends Activity { } private static void logd(String s) { - Rlog.d(TAG, s); + Log.d(TAG, s); } private static void loge(String s) { - Rlog.d(TAG, s); + Log.d(TAG, s); } } diff --git a/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CustomConfigLoader.java b/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CustomConfigLoader.java index 02c61d778086..46b1d5fe27be 100644 --- a/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CustomConfigLoader.java +++ b/packages/CarrierDefaultApp/src/com/android/carrierdefaultapp/CustomConfigLoader.java @@ -19,7 +19,6 @@ import android.content.Context; import android.content.Intent; import android.os.PersistableBundle; import android.telephony.CarrierConfigManager; -import android.telephony.Rlog; import android.text.TextUtils; import android.util.Log; @@ -27,7 +26,6 @@ import com.android.internal.telephony.TelephonyIntents; import com.android.internal.util.ArrayUtils; import java.util.ArrayList; -import java.util.HashMap; import java.util.List; /** @@ -44,7 +42,7 @@ public class CustomConfigLoader { private static final String INTER_GROUP_DELIMITER = "\\s*:\\s*"; private static final String TAG = CustomConfigLoader.class.getSimpleName(); - private static final boolean VDBG = Rlog.isLoggable(TAG, Log.VERBOSE); + private static final boolean VDBG = Log.isLoggable(TAG, Log.VERBOSE); /** * loads and parses the carrier config, return a list of carrier action for the given signal @@ -70,7 +68,7 @@ public class CustomConfigLoader { // return an empty list if no match found List<Integer> actionList = new ArrayList<>(); if (carrierConfigManager == null) { - Rlog.e(TAG, "load carrier config failure with carrier config manager uninitialized"); + Log.e(TAG, "load carrier config failure with carrier config manager uninitialized"); return actionList; } PersistableBundle b = carrierConfigManager.getConfig(); @@ -101,8 +99,8 @@ public class CustomConfigLoader { .EXTRA_DEFAULT_NETWORK_AVAILABLE_KEY, false)); break; default: - Rlog.e(TAG, "load carrier config failure with un-configured key: " + - intent.getAction()); + Log.e(TAG, "load carrier config failure with un-configured key: " + + intent.getAction()); break; } if (!ArrayUtils.isEmpty(configs)) { @@ -111,12 +109,12 @@ public class CustomConfigLoader { matchConfig(config, arg1, arg2, actionList); if (!actionList.isEmpty()) { // return the first match - if (VDBG) Rlog.d(TAG, "found match action list: " + actionList.toString()); + if (VDBG) Log.d(TAG, "found match action list: " + actionList.toString()); return actionList; } } } - Rlog.d(TAG, "no matching entry for signal: " + intent.getAction() + "arg1: " + arg1 + Log.d(TAG, "no matching entry for signal: " + intent.getAction() + "arg1: " + arg1 + "arg2: " + arg2); } return actionList; @@ -166,7 +164,7 @@ public class CustomConfigLoader { try { actionList.add(Integer.parseInt(idx)); } catch (NumberFormatException e) { - Rlog.e(TAG, "NumberFormatException(string: " + idx + " config:" + config + "): " + Log.e(TAG, "NumberFormatException(string: " + idx + " config:" + config + "): " + e); } } diff --git a/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java b/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java index 1b27b52f1fa1..48d34ae7ba47 100644 --- a/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java +++ b/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java @@ -16,6 +16,7 @@ package com.android.externalstorage; +import android.annotation.NonNull; import android.annotation.Nullable; import android.app.usage.StorageStatsManager; import android.content.ContentResolver; @@ -298,6 +299,53 @@ public class ExternalStorageProvider extends FileSystemProvider { return projection != null ? projection : DEFAULT_ROOT_PROJECTION; } + /** + * Check that the directory is the root of storage or blocked file from tree. + * + * @param docId the docId of the directory to be checked + * @return true, should be blocked from tree. Otherwise, false. + */ + @Override + protected boolean shouldBlockFromTree(@NonNull String docId) { + try { + final File dir = getFileForDocId(docId, true /* visible */).getCanonicalFile(); + if (!dir.isDirectory()) { + return false; + } + + final String path = dir.getAbsolutePath(); + + // Block Download folder from tree + if (MediaStore.Downloads.isDownloadDir(path)) { + return true; + } + + final ArrayMap<String, RootInfo> roots = new ArrayMap<>(); + + synchronized (mRootsLock) { + roots.putAll(mRoots); + } + + // block root of storage + for (int i = 0; i < roots.size(); i++) { + RootInfo rootInfo = roots.valueAt(i); + // skip home root + if (TextUtils.equals(rootInfo.rootId, ROOT_ID_HOME)) { + continue; + } + + // block the root of storage + if (TextUtils.equals(path, rootInfo.visiblePath.getAbsolutePath())) { + return true; + } + } + return false; + } catch (IOException e) { + throw new IllegalArgumentException( + "Failed to determine if " + docId + " should block from tree " + ": " + e); + } + } + @Override protected String getDocIdForFile(File file) throws FileNotFoundException { return getDocIdForFileMaybeCreate(file, false); diff --git a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java index 5e2b7c86ee93..17c621e6fbef 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java @@ -51,6 +51,7 @@ import com.android.internal.telephony.RILConstants; import com.android.internal.util.XmlUtils; import com.android.internal.widget.LockPatternUtils; import com.android.internal.widget.LockPatternView; +import com.android.internal.widget.LockscreenCredential; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; @@ -1992,8 +1993,11 @@ class DatabaseHelper extends SQLiteOpenHelper { try { LockPatternUtils lpu = new LockPatternUtils(mContext); List<LockPatternView.Cell> cellPattern = - LockPatternUtils.stringToPattern(lockPattern); - lpu.saveLockPattern(cellPattern, null, UserHandle.USER_SYSTEM); + LockPatternUtils.byteArrayToPattern(lockPattern.getBytes()); + lpu.setLockCredential( + LockscreenCredential.createPattern(cellPattern), + LockscreenCredential.createNone(), + UserHandle.USER_SYSTEM); } catch (IllegalArgumentException e) { // Don't want corrupted lock pattern to hang the reboot process } diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationControllerCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationControllerCompat.java index 2ef042210e67..748f356c25b9 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationControllerCompat.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RecentsAnimationControllerCompat.java @@ -115,4 +115,15 @@ public class RecentsAnimationControllerCompat { Log.e(TAG, "Failed to clean up screenshot of recents animation", e); } } + + /** + * @see {{@link IRecentsAnimationController#setWillFinishToHome(boolean)}}. + */ + public void setWillFinishToHome(boolean willFinishToHome) { + try { + mAnimationController.setWillFinishToHome(willFinishToHome); + } catch (RemoteException e) { + Log.e(TAG, "Failed to set overview reached state", e); + } + } }
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputView.java index d45603fd23ff..ebac3d9da5e1 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputView.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputView.java @@ -33,10 +33,9 @@ import android.widget.LinearLayout; import com.android.internal.util.LatencyTracker; import com.android.internal.widget.LockPatternChecker; import com.android.internal.widget.LockPatternUtils; +import com.android.internal.widget.LockscreenCredential; import com.android.systemui.R; -import java.util.Arrays; - /** * Base class for PIN and password unlock screens. */ @@ -132,19 +131,20 @@ public abstract class KeyguardAbsKeyInputView extends LinearLayout protected void verifyPasswordAndUnlock() { if (mDismissing) return; // already verified but haven't been dismissed; don't do it again. - final byte[] entry = getPasswordText(); + final LockscreenCredential password = + LockscreenCredential.createPassword(getPasswordText()); setPasswordEntryInputEnabled(false); if (mPendingLockCheck != null) { mPendingLockCheck.cancel(false); } final int userId = KeyguardUpdateMonitor.getCurrentUser(); - if (entry.length <= MINIMUM_PASSWORD_LENGTH_BEFORE_REPORT) { + if (password.size() <= MINIMUM_PASSWORD_LENGTH_BEFORE_REPORT) { // to avoid accidental lockout, only count attempts that are long enough to be a // real password. This may require some tweaking. setPasswordEntryInputEnabled(true); onPasswordChecked(userId, false /* matched */, 0, false /* not valid - too short */); - Arrays.fill(entry, (byte) 0); + password.zeroize(); return; } @@ -152,9 +152,9 @@ public abstract class KeyguardAbsKeyInputView extends LinearLayout LatencyTracker.getInstance(mContext).onActionStart(ACTION_CHECK_CREDENTIAL); LatencyTracker.getInstance(mContext).onActionStart(ACTION_CHECK_CREDENTIAL_UNLOCKED); } - mPendingLockCheck = LockPatternChecker.checkPassword( + mPendingLockCheck = LockPatternChecker.checkCredential( mLockPatternUtils, - entry, + password, userId, new LockPatternChecker.OnCheckCallback() { @@ -166,7 +166,7 @@ public abstract class KeyguardAbsKeyInputView extends LinearLayout } onPasswordChecked(userId, true /* matched */, 0 /* timeoutMs */, true /* isValidPassword */); - Arrays.fill(entry, (byte) 0); + password.zeroize(); } @Override @@ -181,7 +181,7 @@ public abstract class KeyguardAbsKeyInputView extends LinearLayout onPasswordChecked(userId, false /* matched */, timeoutMs, true /* isValidPassword */); } - Arrays.fill(entry, (byte) 0); + password.zeroize(); } @Override @@ -192,7 +192,7 @@ public abstract class KeyguardAbsKeyInputView extends LinearLayout LatencyTracker.getInstance(mContext).onActionEnd( ACTION_CHECK_CREDENTIAL_UNLOCKED); } - Arrays.fill(entry, (byte) 0); + password.zeroize(); } }); } @@ -223,7 +223,7 @@ public abstract class KeyguardAbsKeyInputView extends LinearLayout } protected abstract void resetPasswordText(boolean animate, boolean announce); - protected abstract byte[] getPasswordText(); + protected abstract CharSequence getPasswordText(); protected abstract void setPasswordEntryEnabled(boolean enabled); protected abstract void setPasswordEntryInputEnabled(boolean enabled); diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java index e3ac0f684e44..12c9fc9ce536 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPasswordView.java @@ -243,8 +243,8 @@ public class KeyguardPasswordView extends KeyguardAbsKeyInputView } @Override - protected byte[] getPasswordText() { - return charSequenceToByteArray(mPasswordEntry.getText()); + protected CharSequence getPasswordText() { + return mPasswordEntry.getText(); } @Override @@ -379,18 +379,4 @@ public class KeyguardPasswordView extends KeyguardAbsKeyInputView return getContext().getString( com.android.internal.R.string.keyguard_accessibility_password_unlock); } - - /* - * This method avoids creating a new string when getting a byte array from EditView#getText(). - */ - private static byte[] charSequenceToByteArray(CharSequence chars) { - if (chars == null) { - return null; - } - byte[] bytes = new byte[chars.length()]; - for (int i = 0; i < chars.length(); i++) { - bytes[i] = (byte) chars.charAt(i); - } - return bytes; - } } diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternView.java index 297052f842f3..9eb168a7b567 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternView.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternView.java @@ -39,6 +39,7 @@ import com.android.internal.util.LatencyTracker; import com.android.internal.widget.LockPatternChecker; import com.android.internal.widget.LockPatternUtils; import com.android.internal.widget.LockPatternView; +import com.android.internal.widget.LockscreenCredential; import com.android.settingslib.animation.AppearAnimationCreator; import com.android.settingslib.animation.AppearAnimationUtils; import com.android.settingslib.animation.DisappearAnimationUtils; @@ -297,9 +298,9 @@ public class KeyguardPatternView extends LinearLayout implements KeyguardSecurit LatencyTracker.getInstance(mContext).onActionStart(ACTION_CHECK_CREDENTIAL); LatencyTracker.getInstance(mContext).onActionStart(ACTION_CHECK_CREDENTIAL_UNLOCKED); } - mPendingLockCheck = LockPatternChecker.checkPattern( + mPendingLockCheck = LockPatternChecker.checkCredential( mLockPatternUtils, - pattern, + LockscreenCredential.createPattern(pattern), userId, new LockPatternChecker.OnCheckCallback() { diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java index 274f739d8c29..8e9df5563123 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputView.java @@ -167,8 +167,8 @@ public abstract class KeyguardPinBasedInputView extends KeyguardAbsKeyInputView } @Override - protected byte[] getPasswordText() { - return charSequenceToByteArray(mPasswordEntry.getText()); + protected CharSequence getPasswordText() { + return mPasswordEntry.getText(); } @Override @@ -266,18 +266,4 @@ public abstract class KeyguardPinBasedInputView extends KeyguardAbsKeyInputView return getContext().getString( com.android.internal.R.string.keyguard_accessibility_pin_unlock); } - - /* - * This method avoids creating a new string when getting a byte array from EditView#getText(). - */ - private static byte[] charSequenceToByteArray(CharSequence chars) { - if (chars == null) { - return null; - } - byte[] bytes = new byte[chars.length()]; - for (int i = 0; i < chars.length(); i++) { - bytes[i] = (byte) chars.charAt(i); - } - return bytes; - } } diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java index af4e61b3f6bc..5d35169cf926 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSliceView.java @@ -31,6 +31,7 @@ import android.app.PendingIntent; import android.content.Context; import android.graphics.Color; import android.graphics.drawable.Drawable; +import android.graphics.text.LineBreaker; import android.net.Uri; import android.os.Trace; import android.provider.Settings; @@ -152,6 +153,7 @@ public class KeyguardSliceView extends LinearLayout implements View.OnClickListe mRowWithHeaderTextSize = mContext.getResources().getDimensionPixelSize( R.dimen.header_row_font_size); mTitle.setOnClickListener(this); + mTitle.setBreakStrategy(LineBreaker.BREAK_STRATEGY_BALANCED); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java index 3a7a7f72f299..ad209861d273 100644 --- a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java +++ b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java @@ -460,7 +460,7 @@ public class ScreenDecorations extends SystemUI implements Tunable { | WindowManager.LayoutParams.FLAG_SLIPPERY | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN, PixelFormat.TRANSLUCENT); - lp.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS + lp.privateFlags |= WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS | WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION; if (!DEBUG_SCREENSHOT_ROUNDED_CORNERS) { diff --git a/packages/SystemUI/src/com/android/systemui/SizeCompatModeActivityController.java b/packages/SystemUI/src/com/android/systemui/SizeCompatModeActivityController.java index a8591bbb4dec..10009f5f5582 100644 --- a/packages/SystemUI/src/com/android/systemui/SizeCompatModeActivityController.java +++ b/packages/SystemUI/src/com/android/systemui/SizeCompatModeActivityController.java @@ -203,7 +203,7 @@ public class SizeCompatModeActivityController extends SystemUI implements Comman mWinParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL; mWinParams.format = PixelFormat.TRANSLUCENT; - mWinParams.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS; + mWinParams.privateFlags |= WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS; mWinParams.setTitle(SizeCompatModeActivityController.class.getSimpleName() + context.getDisplayId()); } diff --git a/packages/SystemUI/src/com/android/systemui/SysUIToast.java b/packages/SystemUI/src/com/android/systemui/SysUIToast.java index 8bcf0571b2d0..0f7f1bebae57 100644 --- a/packages/SystemUI/src/com/android/systemui/SysUIToast.java +++ b/packages/SystemUI/src/com/android/systemui/SysUIToast.java @@ -31,7 +31,7 @@ public class SysUIToast { public static Toast makeText(Context context, CharSequence text, @Duration int duration) { Toast toast = Toast.makeText(context, text, duration); toast.getWindowParams().privateFlags |= - WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS; + WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS; return toast; } diff --git a/packages/SystemUI/src/com/android/systemui/assist/AssistModule.java b/packages/SystemUI/src/com/android/systemui/assist/AssistModule.java index 739eade35ca6..6f5a17dca432 100644 --- a/packages/SystemUI/src/com/android/systemui/assist/AssistModule.java +++ b/packages/SystemUI/src/com/android/systemui/assist/AssistModule.java @@ -21,6 +21,7 @@ import android.os.Handler; import android.os.HandlerThread; import android.os.SystemClock; +import androidx.annotation.Nullable; import androidx.slice.Clock; import com.android.internal.app.AssistUtils; @@ -68,6 +69,7 @@ public abstract class AssistModule { } @Provides + @Nullable static AssistHandleViewController provideAssistHandleViewController( NavigationBarController navigationBarController) { return navigationBarController.getAssistHandlerViewController(); diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java index a9359d4ff0db..f1abdb31b5f8 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java @@ -580,7 +580,7 @@ public class AuthContainerView extends LinearLayout WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL, WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED, PixelFormat.TRANSLUCENT); - lp.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS; + lp.privateFlags |= WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS; lp.setTitle("BiometricPrompt"); lp.token = windowToken; return lp; diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialPasswordView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialPasswordView.java index 8df072e9b99e..bebaa4b688db 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialPasswordView.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialPasswordView.java @@ -26,7 +26,7 @@ import android.widget.EditText; import android.widget.TextView; import com.android.internal.widget.LockPatternChecker; -import com.android.internal.widget.LockPatternUtils; +import com.android.internal.widget.LockscreenCredential; import com.android.systemui.R; /** @@ -96,13 +96,16 @@ public class AuthCredentialPasswordView extends AuthCredentialView } private void checkPasswordAndUnlock() { - final byte[] password = LockPatternUtils.charSequenceToByteArray(mPasswordField.getText()); - if (password == null || password.length == 0) { - return; - } + try (LockscreenCredential password = mCredentialType == Utils.CREDENTIAL_PIN + ? LockscreenCredential.createPinOrNone(mPasswordField.getText()) + : LockscreenCredential.createPasswordOrNone(mPasswordField.getText())) { + if (password.isNone()) { + return; + } - mPendingLockCheck = LockPatternChecker.checkPassword(mLockPatternUtils, - password, mUserId, this::onCredentialChecked); + mPendingLockCheck = LockPatternChecker.checkCredential(mLockPatternUtils, + password, mUserId, this::onCredentialChecked); + } } @Override diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialPatternView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialPatternView.java index 6c36f8263237..14414a4225de 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialPatternView.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialPatternView.java @@ -22,6 +22,7 @@ import android.util.AttributeSet; import com.android.internal.widget.LockPatternChecker; import com.android.internal.widget.LockPatternUtils; import com.android.internal.widget.LockPatternView; +import com.android.internal.widget.LockscreenCredential; import com.android.systemui.R; import java.util.List; @@ -64,11 +65,13 @@ public class AuthCredentialPatternView extends AuthCredentialView { return; } - mPendingLockCheck = LockPatternChecker.checkPattern( - mLockPatternUtils, - pattern, - mUserId, - this::onPatternChecked); + try (LockscreenCredential credential = LockscreenCredential.createPattern(pattern)) { + mPendingLockCheck = LockPatternChecker.checkCredential( + mLockPatternUtils, + credential, + mUserId, + this::onPatternChecked); + } } private void onPatternChecked(boolean matched, int timeoutMs) { diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java b/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java index ca4ec6d78a07..b069ba337d8b 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeFactory.java @@ -75,7 +75,7 @@ public class DozeFactory { handler, wakeLock, machine, dockManager, dozeLog), createDozeUi(context, host, wakeLock, machine, handler, alarmManager, params, dozeLog), - new DozeScreenState(wrappedService, handler, params, wakeLock), + new DozeScreenState(wrappedService, handler, host, params, wakeLock), createDozeScreenBrightness(context, wrappedService, sensorManager, host, params, handler), new DozeWallpaperState(context, getBiometricUnlockController(dozeService)), diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java b/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java index 07dd2cd77043..1a6bd60816b3 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java @@ -63,9 +63,10 @@ public interface DozeHost { void setDozeScreenBrightness(int value); /** - * Makes scrims black and changes animation durations. + * Fade out screen before switching off the display power mode. + * @param onDisplayOffCallback Executed when the display is black. */ - default void prepareForGentleWakeUp() {} + void prepareForGentleSleep(Runnable onDisplayOffCallback); void onIgnoreTouchWhilePulsing(boolean ignore); diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeScreenState.java b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenState.java index 38ee2fed136d..95c42fcd175c 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeScreenState.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeScreenState.java @@ -16,6 +16,12 @@ package com.android.systemui.doze; +import static com.android.systemui.doze.DozeMachine.State.DOZE; +import static com.android.systemui.doze.DozeMachine.State.DOZE_AOD; +import static com.android.systemui.doze.DozeMachine.State.DOZE_AOD_PAUSED; +import static com.android.systemui.doze.DozeMachine.State.DOZE_AOD_PAUSING; +import static com.android.systemui.doze.DozeMachine.State.DOZE_PULSE_DONE; + import android.os.Handler; import android.util.Log; import android.view.Display; @@ -48,21 +54,24 @@ public class DozeScreenState implements DozeMachine.Part { private final Handler mHandler; private final Runnable mApplyPendingScreenState = this::applyPendingScreenState; private final DozeParameters mParameters; + private final DozeHost mDozeHost; private int mPendingScreenState = Display.STATE_UNKNOWN; private SettableWakeLock mWakeLock; - public DozeScreenState(DozeMachine.Service service, Handler handler, + public DozeScreenState(DozeMachine.Service service, Handler handler, DozeHost host, DozeParameters parameters, WakeLock wakeLock) { mDozeService = service; mHandler = handler; mParameters = parameters; + mDozeHost = host; mWakeLock = new SettableWakeLock(wakeLock, TAG); } @Override public void transitionTo(DozeMachine.State oldState, DozeMachine.State newState) { int screenState = newState.screenState(mParameters); + mDozeHost.prepareForGentleSleep(null); if (newState == DozeMachine.State.FINISH) { // Make sure not to apply the screen state after DozeService was destroyed. @@ -79,12 +88,13 @@ public class DozeScreenState implements DozeMachine.Part { return; } - boolean messagePending = mHandler.hasCallbacks(mApplyPendingScreenState); - boolean pulseEnding = oldState == DozeMachine.State.DOZE_PULSE_DONE - && newState == DozeMachine.State.DOZE_AOD; - boolean turningOn = (oldState == DozeMachine.State.DOZE_AOD_PAUSED - || oldState == DozeMachine.State.DOZE) && newState == DozeMachine.State.DOZE_AOD; - boolean justInitialized = oldState == DozeMachine.State.INITIALIZED; + final boolean messagePending = mHandler.hasCallbacks(mApplyPendingScreenState); + final boolean pulseEnding = oldState == DOZE_PULSE_DONE && newState == DOZE_AOD; + final boolean turningOn = (oldState == DOZE_AOD_PAUSED + || oldState == DOZE) && newState == DOZE_AOD; + final boolean turningOff = (oldState == DOZE_AOD && newState == DOZE) + || (oldState == DOZE_AOD_PAUSING && newState == DOZE_AOD_PAUSED); + final boolean justInitialized = oldState == DozeMachine.State.INITIALIZED; if (messagePending || justInitialized || pulseEnding || turningOn) { // During initialization, we hide the navigation bar. That is however only applied after // a traversal; setting the screen state here is immediate however, so it can happen @@ -93,7 +103,7 @@ public class DozeScreenState implements DozeMachine.Part { mPendingScreenState = screenState; // Delay screen state transitions even longer while animations are running. - boolean shouldDelayTransition = newState == DozeMachine.State.DOZE_AOD + boolean shouldDelayTransition = newState == DOZE_AOD && mParameters.shouldControlScreenOff() && !turningOn; if (shouldDelayTransition) { @@ -114,6 +124,8 @@ public class DozeScreenState implements DozeMachine.Part { } else if (DEBUG) { Log.d(TAG, "Pending display state change to " + screenState); } + } else if (turningOff) { + mDozeHost.prepareForGentleSleep(() -> applyScreenState(screenState)); } else { applyScreenState(screenState); } diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java b/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java index 2c0ccd214188..f1557838fd73 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java @@ -135,7 +135,6 @@ public class DozeUi implements DozeMachine.Part { break; case DOZE: case DOZE_AOD_PAUSED: - mHost.prepareForGentleWakeUp(); unscheduleTimeTick(); break; case DOZE_REQUEST_PULSE: diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipDismissViewController.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipDismissViewController.java index 8224365e7b96..3f15966c7fbb 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipDismissViewController.java +++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipDismissViewController.java @@ -107,7 +107,7 @@ public class PipDismissViewController { | LayoutParams.FLAG_NOT_FOCUSABLE, PixelFormat.TRANSLUCENT); lp.setTitle("pip-dismiss-overlay"); - lp.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS; + lp.privateFlags |= WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS; lp.gravity = Gravity.TOP | Gravity.CENTER_HORIZONTAL; mWindowManager.addView(mDismissView, lp); } diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java index ae83567ed159..2e24403d460f 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java @@ -28,10 +28,12 @@ import android.metrics.LogMaker; import android.os.Bundle; import android.os.Handler; import android.os.Message; +import android.provider.Settings; import android.service.quicksettings.Tile; import android.util.AttributeSet; import android.view.LayoutInflater; import android.view.View; +import android.widget.FrameLayout; import android.widget.LinearLayout; import com.android.internal.logging.MetricsLogger; @@ -49,6 +51,8 @@ import com.android.systemui.qs.customize.QSCustomizer; import com.android.systemui.qs.external.CustomTile; import com.android.systemui.settings.BrightnessController; import com.android.systemui.settings.ToggleSliderView; +import com.android.systemui.shared.plugins.PluginManager; +import com.android.systemui.statusbar.phone.NPVPluginManager; import com.android.systemui.statusbar.policy.BrightnessMirrorController; import com.android.systemui.statusbar.policy.BrightnessMirrorController.BrightnessMirrorListener; import com.android.systemui.tuner.TunerService; @@ -98,6 +102,10 @@ public class QSPanel extends LinearLayout implements Tunable, Callback, Brightne private BrightnessMirrorController mBrightnessMirrorController; private View mDivider; + private FrameLayout mPluginFrame; + private final PluginManager mPluginManager; + private NPVPluginManager mNPVPluginManager; + public QSPanel(Context context) { this(context, null); } @@ -106,9 +114,13 @@ public class QSPanel extends LinearLayout implements Tunable, Callback, Brightne this(context, attrs, null); } + public QSPanel(Context context, AttributeSet attrs, DumpController dumpController) { + this(context, attrs, dumpController, null); + } + @Inject public QSPanel(@Named(VIEW_CONTEXT) Context context, AttributeSet attrs, - DumpController dumpController) { + DumpController dumpController, PluginManager pluginManager) { super(context, attrs); mContext = context; @@ -136,6 +148,15 @@ public class QSPanel extends LinearLayout implements Tunable, Callback, Brightne mBrightnessController = new BrightnessController(getContext(), findViewById(R.id.brightness_slider)); mDumpController = dumpController; + mPluginManager = pluginManager; + if (mPluginManager != null && Settings.System.getInt( + mContext.getContentResolver(), "npv_plugin_flag", 0) == 2) { + mPluginFrame = (FrameLayout) LayoutInflater.from(mContext).inflate( + R.layout.status_bar_expanded_plugin_frame, this, false); + addView(mPluginFrame); + mNPVPluginManager = new NPVPluginManager(mPluginFrame, mPluginManager); + } + } protected void addDivider() { @@ -377,6 +398,7 @@ public class QSPanel extends LinearLayout implements Tunable, Callback, Brightne if (mListening) { refreshAllTiles(); } + if (mNPVPluginManager != null) mNPVPluginManager.setListening(listening); } public void setListening(boolean listening, boolean expanded) { diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsOnboarding.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsOnboarding.java index c1ce16337f8d..aa6444973a6f 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/RecentsOnboarding.java +++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsOnboarding.java @@ -497,7 +497,7 @@ public class RecentsOnboarding { WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY, flags, PixelFormat.TRANSLUCENT); - lp.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS; + lp.privateFlags |= WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS; lp.setTitle("RecentsOnboarding"); lp.gravity = gravity; return lp; diff --git a/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java b/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java index 0f277ca8b2c6..2d1c08719119 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java +++ b/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java @@ -128,7 +128,7 @@ public class ScreenPinningRequest implements View.OnClickListener, | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE, PixelFormat.TRANSLUCENT); lp.token = new Binder(); - lp.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS; + lp.privateFlags |= WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS; lp.setTitle("ScreenPinningConfirmation"); lp.gravity = Gravity.FILL; return lp; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NavigationBarController.java b/packages/SystemUI/src/com/android/systemui/statusbar/NavigationBarController.java index e09e9cd75de2..5144a95a0a51 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NavigationBarController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NavigationBarController.java @@ -233,11 +233,13 @@ public class NavigationBarController implements Callbacks { } /** @return {@link NavigationBarFragment} on the default display. */ + @Nullable public NavigationBarFragment getDefaultNavigationBarFragment() { return mNavigationBars.get(DEFAULT_DISPLAY); } /** @return {@link AssistHandleViewController} (only on the default display). */ + @Nullable public AssistHandleViewController getAssistHandlerViewController() { NavigationBarFragment navBar = getDefaultNavigationBarFragment(); return navBar == null ? null : navBar.getAssistHandlerViewController(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DoubleTapHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DoubleTapHelper.java index 0d62703cbced..78ea5c03a7b5 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DoubleTapHelper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DoubleTapHelper.java @@ -100,6 +100,7 @@ public class DoubleTapHelper { event.getY() - mActivationY); } if (withinDoubleTapSlop) { + makeInactive(); if (!mDoubleTapListener.onDoubleTap()) { return false; } @@ -134,6 +135,7 @@ public class DoubleTapHelper { if (mActivated) { mActivated = false; mActivationListener.onActiveChanged(false); + mView.removeCallbacks(mTapTimeoutRunnable); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java index ca7c227f42b7..442c08991581 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/EdgeBackGestureHandler.java @@ -276,7 +276,7 @@ public class EdgeBackGestureHandler implements DisplayListener { | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN, PixelFormat.TRANSLUCENT); mEdgePanelLp.privateFlags |= - WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS; + WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS; mEdgePanelLp.setTitle(TAG + mDisplayId); mEdgePanelLp.accessibilityTitle = mContext.getString(R.string.nav_bar_edge_panel); mEdgePanelLp.windowAnimations = 0; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/FloatingRotationButton.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/FloatingRotationButton.java index deb314bae5c9..a7849847387d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/FloatingRotationButton.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/FloatingRotationButton.java @@ -82,7 +82,7 @@ public class FloatingRotationButton implements RotationButton { final WindowManager.LayoutParams lp = new WindowManager.LayoutParams(mDiameter, mDiameter, mMargin, mMargin, WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL, flags, PixelFormat.TRANSLUCENT); - lp.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS; + lp.privateFlags |= WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS; lp.setTitle("FloatingRotationButton"); switch (mWindowManager.getDefaultDisplay().getRotation()) { case Surface.ROTATION_0: diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java index 177294b1f45e..9ab635cad418 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java @@ -43,6 +43,7 @@ import android.hardware.biometrics.BiometricSourceType; import android.os.PowerManager; import android.os.SystemClock; import android.provider.DeviceConfig; +import android.provider.Settings; import android.util.AttributeSet; import android.util.Log; import android.util.MathUtils; @@ -541,7 +542,10 @@ public class NotificationPanelView extends PanelView implements mQsNavbarScrim = findViewById(R.id.qs_navbar_scrim); mLastOrientation = getResources().getConfiguration().orientation; mPluginFrame = findViewById(R.id.plugin_frame); - mNPVPluginManager = new NPVPluginManager(mPluginFrame, mPluginManager); + if (Settings.System.getInt( + mContext.getContentResolver(), "npv_plugin_flag", 0) == 1) { + mNPVPluginManager = new NPVPluginManager(mPluginFrame, mPluginManager); + } initBottomArea(); @@ -778,7 +782,7 @@ public class NotificationPanelView extends PanelView implements mPluginFrame.setLayoutParams(lp); } - mNPVPluginManager.replaceFrameLayout(mPluginFrame); + if (mNPVPluginManager != null) mNPVPluginManager.replaceFrameLayout(mPluginFrame); } private void initBottomArea() { @@ -808,8 +812,10 @@ public class NotificationPanelView extends PanelView implements int oldMaxHeight = mQsMaxExpansionHeight; if (mQs != null) { mQsMinExpansionHeight = mKeyguardShowing ? 0 : mQs.getQsMinExpansionHeight(); - mNPVPluginManager.setYOffset(mQsMinExpansionHeight); - mQsMinExpansionHeight += mNPVPluginManager.getHeight(); + if (mNPVPluginManager != null) { + mNPVPluginManager.setYOffset(mQsMinExpansionHeight); + mQsMinExpansionHeight += mNPVPluginManager.getHeight(); + } mQsMaxExpansionHeight = mQs.getDesiredHeight(); mNotificationStackScroller.setMaxTopPadding( mQsMaxExpansionHeight + mQsNotificationTopPadding); @@ -1915,9 +1921,11 @@ public class NotificationPanelView extends PanelView implements mBarState != StatusBarState.KEYGUARD && (!mQsExpanded || mQsExpansionFromOverscroll)); updateEmptyShadeView(); - mNPVPluginManager.changeVisibility((mBarState != StatusBarState.KEYGUARD) - ? View.VISIBLE - : View.INVISIBLE); + if (mNPVPluginManager != null) { + mNPVPluginManager.changeVisibility((mBarState != StatusBarState.KEYGUARD) + ? View.VISIBLE + : View.INVISIBLE); + } mQsNavbarScrim.setVisibility(mBarState == StatusBarState.SHADE && mQsExpanded && !mStackScrollerOverscrolling && mQsScrimEnabled ? View.VISIBLE @@ -1975,7 +1983,9 @@ public class NotificationPanelView extends PanelView implements float qsExpansionFraction = getQsExpansionFraction(); mQs.setQsExpansion(qsExpansionFraction, getHeaderTranslation()); int heightDiff = mQs.getDesiredHeight() - mQs.getQsMinExpansionHeight(); - mNPVPluginManager.setExpansion(qsExpansionFraction, getHeaderTranslation(), heightDiff); + if (mNPVPluginManager != null) { + mNPVPluginManager.setExpansion(qsExpansionFraction, getHeaderTranslation(), heightDiff); + } mNotificationStackScroller.setQsExpansionFraction(qsExpansionFraction); } @@ -2396,7 +2406,7 @@ public class NotificationPanelView extends PanelView implements appearAmount = mNotificationStackScroller.calculateAppearFractionBypass(); } startHeight = -mQs.getQsMinExpansionHeight(); - startHeight -= mNPVPluginManager.getHeight(); + if (mNPVPluginManager != null) startHeight -= mNPVPluginManager.getHeight(); } float translation = MathUtils.lerp(startHeight, 0, Math.min(1.0f, appearAmount)) @@ -2540,7 +2550,7 @@ public class NotificationPanelView extends PanelView implements mKeyguardStatusBar.setListening(listening); if (mQs == null) return; mQs.setListening(listening); - mNPVPluginManager.setListening(listening); + if (mNPVPluginManager != null) mNPVPluginManager.setListening(listening); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java index 5ba19cca6532..e7d896c2b88d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java @@ -258,7 +258,7 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, OnCo final ScrimState oldState = mState; mState = state; - Trace.traceCounter(Trace.TRACE_TAG_APP, "scrim_state", mState.getIndex()); + Trace.traceCounter(Trace.TRACE_TAG_APP, "scrim_state", mState.ordinal()); if (mCallback != null) { mCallback.onCancelled(); @@ -519,22 +519,6 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, OnCo } /** - * Set front scrim to black, cancelling animations, in order to prepare to fade them - * away once the display turns on. - */ - public void prepareForGentleWakeUp() { - if (mState == ScrimState.AOD && mDozeParameters.getAlwaysOn()) { - mInFrontAlpha = 1f; - mInFrontTint = Color.BLACK; - mBehindTint = Color.BLACK; - mAnimateChange = false; - updateScrims(); - mAnimateChange = true; - mAnimationDuration = ANIMATION_DURATION_LONG; - } - } - - /** * If the lock screen sensor is active. */ public void setWakeLockScreenSensorActive(boolean active) { @@ -595,6 +579,8 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, OnCo setScrimAlpha(mScrimInFront, mInFrontAlpha); setScrimAlpha(mScrimBehind, mBehindAlpha); setScrimAlpha(mScrimForBubble, mBubbleAlpha); + // The animation could have all already finished, let's call onFinished just in case + onFinished(); dispatchScrimsVisible(); } @@ -693,9 +679,9 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, OnCo @Override public void onAnimationEnd(Animator animation) { + scrim.setTag(TAG_KEY_ANIM, null); onFinished(lastCallback); - scrim.setTag(TAG_KEY_ANIM, null); dispatchScrimsVisible(); if (!mDeferFinishedListener && mOnAnimationFinished != null) { @@ -759,9 +745,9 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, OnCo } private void onFinished(Callback callback) { - if (!hasReachedFinalState(mScrimBehind) - || !hasReachedFinalState(mScrimInFront) - || !hasReachedFinalState(mScrimForBubble)) { + if (isAnimating(mScrimBehind) + || isAnimating(mScrimInFront) + || isAnimating(mScrimForBubble)) { if (callback != null && callback != mCallback) { // Since we only notify the callback that we're finished once everything has // finished, we need to make sure that any changing callbacks are also invoked @@ -794,11 +780,6 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, OnCo } } - private boolean hasReachedFinalState(ScrimView scrim) { - return scrim.getViewAlpha() == getCurrentScrimAlpha(scrim) - && scrim.getTint() == getCurrentScrimTint(scrim); - } - private boolean isAnimating(View scrim) { return scrim.getTag(TAG_KEY_ANIM) != null; } @@ -854,10 +835,7 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, OnCo } else { // update the alpha directly updateScrimColor(scrim, alpha, getCurrentScrimTint(scrim)); - onFinished(); } - } else { - onFinished(); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java index 7463c7c232bd..e059715986dc 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java @@ -30,12 +30,30 @@ public enum ScrimState { /** * Initial state. */ - UNINITIALIZED(-1), + UNINITIALIZED, + + /** + * When turned off by sensors (prox, presence.) + */ + OFF { + @Override + public void prepare(ScrimState previousState) { + mFrontTint = Color.BLACK; + mBehindTint = previousState.mBehindTint; + mBubbleTint = previousState.mBubbleTint; + + mFrontAlpha = 1f; + mBehindAlpha = previousState.mBehindAlpha; + mBubbleAlpha = previousState.mBubbleAlpha; + + mAnimationDuration = ScrimController.ANIMATION_DURATION_LONG; + } + }, /** * On the lock screen. */ - KEYGUARD(0) { + KEYGUARD { @Override public void prepare(ScrimState previousState) { mBlankScreen = false; @@ -65,7 +83,7 @@ public enum ScrimState { /** * Showing password challenge on the keyguard. */ - BOUNCER(1) { + BOUNCER { @Override public void prepare(ScrimState previousState) { mBehindAlpha = ScrimController.GRADIENT_SCRIM_ALPHA_BUSY; @@ -77,7 +95,7 @@ public enum ScrimState { /** * Showing password challenge on top of a FLAG_SHOW_WHEN_LOCKED activity. */ - BOUNCER_SCRIMMED(2) { + BOUNCER_SCRIMMED { @Override public void prepare(ScrimState previousState) { mBehindAlpha = 0; @@ -89,7 +107,7 @@ public enum ScrimState { /** * Changing screen brightness from quick settings. */ - BRIGHTNESS_MIRROR(3) { + BRIGHTNESS_MIRROR { @Override public void prepare(ScrimState previousState) { mBehindAlpha = 0; @@ -101,7 +119,7 @@ public enum ScrimState { /** * Always on display or screen off. */ - AOD(4) { + AOD { @Override public void prepare(ScrimState previousState) { final boolean alwaysOnEnabled = mDozeParameters.getAlwaysOn(); @@ -136,7 +154,7 @@ public enum ScrimState { /** * When phone wakes up because you received a notification. */ - PULSING(5) { + PULSING { @Override public void prepare(ScrimState previousState) { mFrontAlpha = mAodFrontScrimAlpha; @@ -164,7 +182,7 @@ public enum ScrimState { /** * Unlocked on top of an app (launcher or any other activity.) */ - UNLOCKED(6) { + UNLOCKED { @Override public void prepare(ScrimState previousState) { // State that UI will sync to. @@ -201,7 +219,7 @@ public enum ScrimState { /** * Unlocked with a bubble expanded. */ - BUBBLE_EXPANDED(7) { + BUBBLE_EXPANDED { @Override public void prepare(ScrimState previousState) { mFrontTint = Color.TRANSPARENT; @@ -237,17 +255,12 @@ public enum ScrimState { DozeParameters mDozeParameters; boolean mDisplayRequiresBlanking; boolean mWallpaperSupportsAmbientMode; - int mIndex; boolean mHasBackdrop; boolean mLaunchingAffordanceWithPreview; boolean mWakeLockScreenSensorActive; boolean mKeyguardFadingAway; long mKeyguardFadingAwayDuration; - ScrimState(int index) { - mIndex = index; - } - public void init(ScrimView scrimInFront, ScrimView scrimBehind, ScrimView scrimForBubble, DozeParameters dozeParameters) { mScrimInFront = scrimInFront; @@ -262,10 +275,6 @@ public enum ScrimState { public void prepare(ScrimState previousState) { } - public int getIndex() { - return mIndex; - } - public float getFrontAlpha() { return mFrontAlpha; } 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 9093687c367a..d1fe46e9cefe 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java @@ -4018,6 +4018,13 @@ public class StatusBar extends SystemUI implements DemoMode, } else if (isPulsing()) { mScrimController.transitionTo(ScrimState.PULSING, mDozeScrimController.getScrimCallback()); + } else if (mDozeServiceHost.hasPendingScreenOffCallback()) { + mScrimController.transitionTo(ScrimState.OFF, new ScrimController.Callback() { + @Override + public void onFinished() { + mDozeServiceHost.executePendingScreenOffCallback(); + } + }); } else if (mDozing && !unlocking) { mScrimController.transitionTo(ScrimState.AOD); } else if (mIsKeyguard && !unlocking) { @@ -4044,6 +4051,7 @@ public class StatusBar extends SystemUI implements DemoMode, private boolean mAnimateWakeup; private boolean mAnimateScreenOff; private boolean mIgnoreTouchWhilePulsing; + private Runnable mPendingScreenOffCallback; @VisibleForTesting boolean mWakeLockScreenPerformsAuth = SystemProperties.getBoolean( "persist.sysui.wake_performs_auth", true); @@ -4266,8 +4274,33 @@ public class StatusBar extends SystemUI implements DemoMode, } @Override - public void prepareForGentleWakeUp() { - mScrimController.prepareForGentleWakeUp(); + public void prepareForGentleSleep(Runnable onDisplayOffCallback) { + if (onDisplayOffCallback != null) { + Log.w(TAG, "Overlapping onDisplayOffCallback. Ignoring previous one."); + } + mPendingScreenOffCallback = onDisplayOffCallback; + updateScrimController(); + } + + /** + * When the dozing host is waiting for scrims to fade out to change the display state. + */ + boolean hasPendingScreenOffCallback() { + return mPendingScreenOffCallback != null; + } + + /** + * Executes an nullifies the pending display state callback. + * + * @see #hasPendingScreenOffCallback() + * @see #prepareForGentleSleep(Runnable) + */ + void executePendingScreenOffCallback() { + if (mPendingScreenOffCallback == null) { + return; + } + mPendingScreenOffCallback.run(); + mPendingScreenOffCallback = null; } private void dispatchTap(View view, float x, float y) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java index ce929b7c621b..44be6bc282a7 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java @@ -93,10 +93,10 @@ public class SystemUIDialog extends AlertDialog { public static void setShowForAllUsers(Dialog dialog, boolean show) { if (show) { dialog.getWindow().getAttributes().privateFlags |= - WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS; + WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS; } else { dialog.getWindow().getAttributes().privateFlags &= - ~WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS; + ~WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS; } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java index 43795dc08c91..cca9479d20d6 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java @@ -23,12 +23,16 @@ import android.app.ActivityManager; import android.app.Notification; import android.app.PendingIntent; import android.app.RemoteInput; +import android.content.ClipDescription; import android.content.Context; import android.content.Intent; +import android.content.pm.PackageManager; import android.content.pm.ShortcutManager; import android.graphics.Rect; import android.graphics.drawable.Drawable; +import android.net.Uri; import android.os.Bundle; +import android.os.ServiceManager; import android.os.SystemClock; import android.os.UserHandle; import android.text.Editable; @@ -53,8 +57,13 @@ import android.widget.LinearLayout; import android.widget.ProgressBar; import android.widget.TextView; +import androidx.core.view.inputmethod.EditorInfoCompat; +import androidx.core.view.inputmethod.InputConnectionCompat; +import androidx.core.view.inputmethod.InputContentInfoCompat; + import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto; +import com.android.internal.statusbar.IStatusBarService; import com.android.systemui.Dependency; import com.android.systemui.Interpolators; import com.android.systemui.R; @@ -65,6 +74,7 @@ import com.android.systemui.statusbar.notification.row.wrapper.NotificationViewW import com.android.systemui.statusbar.notification.stack.StackStateAnimator; import com.android.systemui.statusbar.phone.LightBarController; +import java.util.HashMap; import java.util.function.Consumer; /** @@ -88,6 +98,8 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene private RemoteInputController mController; private RemoteInputQuickSettingsDisabler mRemoteInputQuickSettingsDisabler; + private IStatusBarService mStatusBarManagerService; + private NotificationEntry mEntry; private boolean mRemoved; @@ -103,6 +115,8 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene public RemoteInputView(Context context, AttributeSet attrs) { super(context, attrs); mRemoteInputQuickSettingsDisabler = Dependency.get(RemoteInputQuickSettingsDisabler.class); + mStatusBarManagerService = IStatusBarService.Stub.asInterface( + ServiceManager.getService(Context.STATUS_BAR_SERVICE)); } @Override @@ -128,7 +142,7 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene if (isSoftImeEvent || isKeyboardEnterKey) { if (mEditText.length() > 0) { - sendRemoteInput(); + sendRemoteInput(prepareRemoteInputFromText()); } // Consume action to prevent IME from closing. return true; @@ -141,7 +155,7 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene mEditText.mRemoteInputView = this; } - private void sendRemoteInput() { + protected Intent prepareRemoteInputFromText() { Bundle results = new Bundle(); results.putString(mRemoteInput.getResultKey(), mEditText.getText().toString()); Intent fillInIntent = new Intent().addFlags(Intent.FLAG_RECEIVER_FOREGROUND); @@ -153,6 +167,25 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene RemoteInput.setResultsSource(fillInIntent, RemoteInput.SOURCE_CHOICE); } + return fillInIntent; + } + + protected Intent prepareRemoteInputFromData(String contentType, Uri data) { + HashMap<String, Uri> results = new HashMap<>(); + results.put(contentType, data); + try { + mStatusBarManagerService.grantInlineReplyUriPermission( + mEntry.notification.getKey(), data); + } catch (Exception e) { + Log.e(TAG, "Failed to grant URI permissions:" + e.getMessage(), e); + } + Intent fillInIntent = new Intent().addFlags(Intent.FLAG_RECEIVER_FOREGROUND); + RemoteInput.addDataResultToIntent(mRemoteInput, fillInIntent, results); + + return fillInIntent; + } + + private void sendRemoteInput(Intent intent) { mEditText.setEnabled(false); mSendButton.setVisibility(INVISIBLE); mProgressBar.setVisibility(VISIBLE); @@ -176,7 +209,7 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene MetricsLogger.action(mContext, MetricsProto.MetricsEvent.ACTION_REMOTE_INPUT_SEND, mEntry.notification.getPackageName()); try { - mPendingIntent.send(mContext, 0, fillInIntent); + mPendingIntent.send(mContext, 0, intent); } catch (PendingIntent.CanceledException e) { Log.i(TAG, "Unable to send remote input result", e); MetricsLogger.action(mContext, MetricsProto.MetricsEvent.ACTION_REMOTE_INPUT_FAIL, @@ -195,7 +228,9 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene LayoutInflater.from(context).inflate(R.layout.remote_input, root, false); v.mController = controller; v.mEntry = entry; - v.mEditText.setTextOperationUser(computeTextOperationUser(entry.notification.getUser())); + UserHandle user = computeTextOperationUser(entry.notification.getUser()); + v.mEditText.mUser = user; + v.mEditText.setTextOperationUser(user); v.setTag(VIEW_TAG); return v; @@ -204,7 +239,7 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene @Override public void onClick(View v) { if (v == mSendButton) { - sendRemoteInput(); + sendRemoteInput(prepareRemoteInputFromText()); } } @@ -518,6 +553,7 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene private RemoteInputView mRemoteInputView; boolean mShowImeOnInputConnection; private LightBarController mLightBarController; + UserHandle mUser; public RemoteEditText(Context context, AttributeSet attrs) { super(context, attrs); @@ -617,11 +653,47 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene @Override public InputConnection onCreateInputConnection(EditorInfo outAttrs) { + String[] allowedDataTypes = mRemoteInputView.mRemoteInput.getAllowedDataTypes() + .toArray(new String[0]); + EditorInfoCompat.setContentMimeTypes(outAttrs, allowedDataTypes); final InputConnection inputConnection = super.onCreateInputConnection(outAttrs); - if (mShowImeOnInputConnection && inputConnection != null) { + final InputConnectionCompat.OnCommitContentListener callback = + new InputConnectionCompat.OnCommitContentListener() { + @Override + public boolean onCommitContent( + InputContentInfoCompat inputContentInfoCompat, int i, + Bundle bundle) { + Uri contentUri = inputContentInfoCompat.getContentUri(); + ClipDescription description = inputContentInfoCompat.getDescription(); + String mimeType = null; + if (description != null && description.getMimeTypeCount() > 0) { + mimeType = description.getMimeType(0); + } + if (mimeType != null) { + Intent dataIntent = mRemoteInputView.prepareRemoteInputFromData( + mimeType, contentUri); + mRemoteInputView.sendRemoteInput(dataIntent); + } + return true; + } + }; + + InputConnection ic = InputConnectionCompat.createWrapper( + inputConnection, outAttrs, callback); + + Context userContext = null; + try { + userContext = mContext.createPackageContextAsUser( + mContext.getPackageName(), 0, mUser); + } catch (PackageManager.NameNotFoundException e) { + Log.e(TAG, "Unable to create user context:" + e.getMessage(), e); + } + + if (mShowImeOnInputConnection && ic != null) { + Context targetContext = userContext != null ? userContext : getContext(); final InputMethodManager imm = - getContext().getSystemService(InputMethodManager.class); + targetContext.getSystemService(InputMethodManager.class); if (imm != null) { // onCreateInputConnection is called by InputMethodManager in the middle of // setting up the connection to the IME; wait with requesting the IME until that @@ -636,7 +708,7 @@ public class RemoteInputView extends LinearLayout implements View.OnClickListene } } - return inputConnection; + return ic; } @Override diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeDockHandlerTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeDockHandlerTest.java index 1ce01729f2e9..98ec45947f79 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeDockHandlerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeDockHandlerTest.java @@ -20,13 +20,11 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doReturn; -import static org.mockito.Mockito.mock; 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.Instrumentation; import android.hardware.display.AmbientDisplayConfiguration; import android.os.Handler; import android.os.Looper; @@ -34,7 +32,6 @@ import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import android.testing.TestableLooper.RunWithLooper; -import androidx.test.InstrumentationRegistry; import androidx.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; @@ -45,23 +42,24 @@ import com.android.systemui.doze.DozeMachine.State; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; @SmallTest @RunWith(AndroidTestingRunner.class) @RunWithLooper public class DozeDockHandlerTest extends SysuiTestCase { - private DozeDockHandler mDockHandler; + @Mock private DozeMachine mMachine; - private DozeHostFake mHost; + @Mock + private DozeHost mHost; private AmbientDisplayConfiguration mConfig; - private Instrumentation mInstrumentation; private DockManagerFake mDockManagerFake; + private DozeDockHandler mDockHandler; @Before public void setUp() throws Exception { - mInstrumentation = InstrumentationRegistry.getInstrumentation(); - mMachine = mock(DozeMachine.class); - mHost = spy(new DozeHostFake()); + MockitoAnnotations.initMocks(this); mConfig = DozeConfigurationUtil.createMockConfig(); doReturn(false).when(mConfig).alwaysOnEnabled(anyInt()); diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeHostFake.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeHostFake.java deleted file mode 100644 index abfa755671db..000000000000 --- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeHostFake.java +++ /dev/null @@ -1,120 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.systemui.doze; - -import android.annotation.NonNull; - -/** - * A rudimentary fake for DozeHost. - */ -class DozeHostFake implements DozeHost { - Callback callback; - boolean pulseExtended; - boolean animateWakeup; - boolean animateScreenOff; - boolean dozing; - float doubleTapX; - float doubleTapY; - float aodDimmingScrimOpacity; - - @Override - public void addCallback(@NonNull Callback callback) { - this.callback = callback; - } - - @Override - public void removeCallback(@NonNull Callback callback) { - this.callback = null; - } - - @Override - public void startDozing() { - dozing = true; - } - - @Override - public void pulseWhileDozing(@NonNull PulseCallback callback, int reason) { - throw new RuntimeException("not implemented"); - } - - @Override - public void stopDozing() { - dozing = false; - } - - @Override - public void dozeTimeTick() { - // Nothing to do in here. Real host would just update the UI. - } - - @Override - public boolean isPowerSaveActive() { - return false; - } - - @Override - public boolean isPulsingBlocked() { - return false; - } - - @Override - public boolean isProvisioned() { - return false; - } - - @Override - public boolean isBlockingDoze() { - return false; - } - - @Override - public void onIgnoreTouchWhilePulsing(boolean ignore) { - } - - @Override - public void extendPulse(int reason) { - pulseExtended = true; - } - - @Override - public void stopPulsing() {} - - @Override - public void setAnimateWakeup(boolean animateWakeup) { - this.animateWakeup = animateWakeup; - } - - @Override - public void setAnimateScreenOff(boolean animateScreenOff) { - this.animateScreenOff = animateScreenOff; - } - - @Override - public void onSlpiTap(float x, float y) { - doubleTapX = y; - doubleTapY = y; - } - - @Override - public void setDozeScreenBrightness(int value) { - } - - @Override - public void setAodDimmingScrim(float scrimOpacity) { - aodDimmingScrimOpacity = scrimOpacity; - } -} diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java index aa62e9aca4fe..316b891080ce 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenBrightnessTest.java @@ -30,6 +30,11 @@ import static com.android.systemui.doze.DozeMachine.State.UNINITIALIZED; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.reset; +import static org.mockito.Mockito.verify; import android.content.Intent; import android.os.PowerManager; @@ -45,6 +50,8 @@ import com.android.systemui.util.sensors.FakeSensorManager; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; @RunWith(AndroidJUnit4.class) @SmallTest @@ -55,22 +62,27 @@ public class DozeScreenBrightnessTest extends SysuiTestCase { static final int[] SENSOR_TO_OPACITY = new int[]{-1, 10, 0, 0, 0}; DozeServiceFake mServiceFake; - DozeScreenBrightness mScreen; FakeSensorManager.FakeGenericSensor mSensor; FakeSensorManager mSensorManager; - DozeHostFake mHostFake; + @Mock + DozeHost mDozeHost; + DozeScreenBrightness mScreen; @Before public void setUp() throws Exception { + MockitoAnnotations.initMocks(this); Settings.System.putIntForUser(mContext.getContentResolver(), Settings.System.SCREEN_BRIGHTNESS, DEFAULT_BRIGHTNESS, UserHandle.USER_CURRENT); + doAnswer(invocation -> { + ((Runnable) invocation.getArgument(0)).run(); + return null; + }).when(mDozeHost).prepareForGentleSleep(any()); mServiceFake = new DozeServiceFake(); - mHostFake = new DozeHostFake(); mSensorManager = new FakeSensorManager(mContext); mSensor = mSensorManager.getFakeLightSensor(); mScreen = new DozeScreenBrightness(mContext, mServiceFake, mSensorManager, - mSensor.getSensor(), mHostFake, null /* handler */, + mSensor.getSensor(), mDozeHost, null /* handler */, DEFAULT_BRIGHTNESS, SENSOR_TO_BRIGHTNESS, SENSOR_TO_OPACITY, true /* debuggable */); } @@ -173,7 +185,7 @@ public class DozeScreenBrightnessTest extends SysuiTestCase { @Test public void testNullSensor() throws Exception { mScreen = new DozeScreenBrightness(mContext, mServiceFake, mSensorManager, - null /* sensor */, mHostFake, null /* handler */, + null /* sensor */, mDozeHost, null /* handler */, DEFAULT_BRIGHTNESS, SENSOR_TO_BRIGHTNESS, SENSOR_TO_OPACITY, true /* debuggable */); @@ -203,26 +215,7 @@ public class DozeScreenBrightnessTest extends SysuiTestCase { mSensor.sendSensorEvent(0); assertEquals(1, mServiceFake.screenBrightness); - assertEquals(10/255f, mHostFake.aodDimmingScrimOpacity, 0.001f /* delta */); - } - - @Test - public void pausingAod_softBlanks() throws Exception { - mScreen.transitionTo(UNINITIALIZED, INITIALIZED); - mScreen.transitionTo(INITIALIZED, DOZE_AOD); - - mSensor.sendSensorEvent(2); - - mScreen.transitionTo(DOZE_AOD, DOZE_AOD_PAUSING); - mScreen.transitionTo(DOZE_AOD_PAUSING, DOZE_AOD_PAUSED); - - assertEquals(1f, mHostFake.aodDimmingScrimOpacity, 0.001f /* delta */); - - mSensor.sendSensorEvent(0); - assertEquals(1f, mHostFake.aodDimmingScrimOpacity, 0.001f /* delta */); - - mScreen.transitionTo(DOZE_AOD_PAUSED, DOZE_AOD); - assertEquals(1f, mHostFake.aodDimmingScrimOpacity, 0.001f /* delta */); + verify(mDozeHost).setAodDimmingScrim(eq(10f / 255f)); } @Test @@ -232,8 +225,9 @@ public class DozeScreenBrightnessTest extends SysuiTestCase { mScreen.transitionTo(DOZE_AOD, DOZE_AOD_PAUSING); mScreen.transitionTo(DOZE_AOD_PAUSING, DOZE_AOD_PAUSED); + reset(mDozeHost); mSensor.sendSensorEvent(1); - assertEquals(1f, mHostFake.aodDimmingScrimOpacity, 0.001f /* delta */); + verify(mDozeHost).setAodDimmingScrim(eq(1f)); } @Test @@ -241,11 +235,12 @@ public class DozeScreenBrightnessTest extends SysuiTestCase { mScreen.transitionTo(UNINITIALIZED, INITIALIZED); mScreen.transitionTo(INITIALIZED, DOZE_AOD); mScreen.transitionTo(DOZE_AOD, DOZE); - assertEquals(1f, mHostFake.aodDimmingScrimOpacity, 0.001f /* delta */); + verify(mDozeHost).setAodDimmingScrim(eq(1f)); + reset(mDozeHost); mScreen.transitionTo(DOZE, DOZE_AOD); mSensor.sendSensorEvent(2); - assertEquals(0f, mHostFake.aodDimmingScrimOpacity, 0.001f /* delta */); + verify(mDozeHost).setAodDimmingScrim(eq(0f)); } @Test @@ -260,11 +255,10 @@ public class DozeScreenBrightnessTest extends SysuiTestCase { mSensor.sendSensorEvent(0); + reset(mDozeHost); mScreen.transitionTo(DOZE_AOD_PAUSED, DOZE_AOD); - mSensor.sendSensorEvent(2); - - assertEquals(0f, mHostFake.aodDimmingScrimOpacity, 0.001f /* delta */); + verify(mDozeHost).setAodDimmingScrim(eq(0f)); } @Test @@ -273,11 +267,11 @@ public class DozeScreenBrightnessTest extends SysuiTestCase { mScreen.transitionTo(INITIALIZED, DOZE_AOD); mSensor.sendSensorEvent(2); - mScreen.transitionTo(DOZE_AOD, DOZE_AOD_PAUSING); mScreen.transitionTo(DOZE_AOD_PAUSING, DOZE_AOD_PAUSED); - mScreen.transitionTo(DOZE_AOD_PAUSED, DOZE_AOD); - assertEquals(0f, mHostFake.aodDimmingScrimOpacity, 0.001f /* delta */); + reset(mDozeHost); + mScreen.transitionTo(DOZE_AOD_PAUSED, DOZE_AOD); + verify(mDozeHost).setAodDimmingScrim(eq(0f)); } }
\ No newline at end of file diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenStateTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenStateTest.java index bfd448a2926d..b92f173e8002 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenStateTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeScreenStateTest.java @@ -18,6 +18,8 @@ package com.android.systemui.doze; import static com.android.systemui.doze.DozeMachine.State.DOZE; import static com.android.systemui.doze.DozeMachine.State.DOZE_AOD; +import static com.android.systemui.doze.DozeMachine.State.DOZE_AOD_PAUSED; +import static com.android.systemui.doze.DozeMachine.State.DOZE_AOD_PAUSING; import static com.android.systemui.doze.DozeMachine.State.DOZE_PULSING; import static com.android.systemui.doze.DozeMachine.State.DOZE_REQUEST_PULSE; import static com.android.systemui.doze.DozeMachine.State.FINISH; @@ -30,6 +32,9 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.os.Looper; @@ -46,6 +51,7 @@ import com.android.systemui.utils.os.FakeHandler; 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; @@ -53,12 +59,14 @@ import org.mockito.MockitoAnnotations; @SmallTest public class DozeScreenStateTest extends SysuiTestCase { - DozeServiceFake mServiceFake; - DozeScreenState mScreen; - FakeHandler mHandlerFake; + private DozeServiceFake mServiceFake; + private FakeHandler mHandlerFake; @Mock - DozeParameters mDozeParameters; - WakeLockFake mWakeLock; + private DozeHost mDozeHost; + @Mock + private DozeParameters mDozeParameters; + private WakeLockFake mWakeLock; + private DozeScreenState mScreen; @Before public void setUp() throws Exception { @@ -68,7 +76,8 @@ public class DozeScreenStateTest extends SysuiTestCase { mServiceFake = new DozeServiceFake(); mHandlerFake = new FakeHandler(Looper.getMainLooper()); mWakeLock = new WakeLockFake(); - mScreen = new DozeScreenState(mServiceFake, mHandlerFake, mDozeParameters, mWakeLock); + mScreen = new DozeScreenState(mServiceFake, mHandlerFake, mDozeHost, mDozeParameters, + mWakeLock); } @Test @@ -183,4 +192,20 @@ public class DozeScreenStateTest extends SysuiTestCase { assertThat(mWakeLock.isHeld(), is(false)); } + @Test + public void test_animatesPausing() { + ArgumentCaptor<Runnable> captor = ArgumentCaptor.forClass(Runnable.class); + doAnswer(invocation -> null).when(mDozeHost).prepareForGentleSleep(captor.capture()); + mHandlerFake.setMode(QUEUEING); + + mScreen.transitionTo(UNINITIALIZED, INITIALIZED); + mScreen.transitionTo(INITIALIZED, DOZE_AOD_PAUSING); + mScreen.transitionTo(DOZE_AOD_PAUSING, DOZE_AOD_PAUSED); + + mHandlerFake.dispatchQueuedMessages(); + verify(mDozeHost).prepareForGentleSleep(eq(captor.getValue())); + captor.getValue().run(); + assertEquals(Display.STATE_OFF, mServiceFake.screenState); + } + }
\ No newline at end of file diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java index e5ae6d50c3e5..777fec75d159 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java @@ -20,6 +20,7 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.clearInvocations; +import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.spy; @@ -50,14 +51,22 @@ import com.android.systemui.util.wakelock.WakeLockFake; 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; @SmallTest @RunWith(AndroidTestingRunner.class) @RunWithLooper(setAsMainLooper = true) public class DozeTriggersTest extends SysuiTestCase { - private DozeTriggers mTriggers; + + @Mock private DozeMachine mMachine; - private DozeHostFake mHost; + @Mock + private DozeHost mHost; + @Mock + private AlarmManager mAlarmManager; + private DozeTriggers mTriggers; private FakeSensorManager mSensors; private Sensor mTapSensor; private DockManager mDockManagerFake; @@ -65,9 +74,7 @@ public class DozeTriggersTest extends SysuiTestCase { @Before public void setUp() throws Exception { - mMachine = mock(DozeMachine.class); - AlarmManager alarmManager = mock(AlarmManager.class); - mHost = spy(new DozeHostFake()); + MockitoAnnotations.initMocks(this); AmbientDisplayConfiguration config = DozeConfigurationUtil.createMockConfig(); DozeParameters parameters = DozeConfigurationUtil.createMockParameters(); mSensors = spy(new FakeSensorManager(mContext)); @@ -78,7 +85,7 @@ public class DozeTriggersTest extends SysuiTestCase { new AsyncSensorManager(mSensors, null, new Handler()); mProximitySensor = new FakeProximitySensor(getContext(), asyncSensorManager); - mTriggers = new DozeTriggers(mContext, mMachine, mHost, alarmManager, config, parameters, + mTriggers = new DozeTriggers(mContext, mMachine, mHost, mAlarmManager, config, parameters, asyncSensorManager, Handler.createAsync(Looper.myLooper()), wakeLock, true, mDockManagerFake, mProximitySensor, mock(DozeLog.class)); waitForSensorManager(); @@ -87,19 +94,21 @@ public class DozeTriggersTest extends SysuiTestCase { @Test public void testOnNotification_stillWorksAfterOneFailedProxCheck() throws Exception { when(mMachine.getState()).thenReturn(DozeMachine.State.DOZE); + ArgumentCaptor<DozeHost.Callback> captor = ArgumentCaptor.forClass(DozeHost.Callback.class); + doAnswer(invocation -> null).when(mHost).addCallback(captor.capture()); mTriggers.transitionTo(DozeMachine.State.UNINITIALIZED, DozeMachine.State.INITIALIZED); mTriggers.transitionTo(DozeMachine.State.INITIALIZED, DozeMachine.State.DOZE); clearInvocations(mMachine); mProximitySensor.setLastEvent(new ProximitySensor.ProximityEvent(true, 1)); - mHost.callback.onNotificationAlerted(null /* pulseSuppressedListener */); + captor.getValue().onNotificationAlerted(null /* pulseSuppressedListener */); mProximitySensor.alertListeners(); verify(mMachine, never()).requestState(any()); verify(mMachine, never()).requestPulse(anyInt()); - mHost.callback.onNotificationAlerted(null /* pulseSuppressedListener */); + captor.getValue().onNotificationAlerted(null /* pulseSuppressedListener */); waitForSensorManager(); mProximitySensor.setLastEvent(new ProximitySensor.ProximityEvent(false, 2)); mProximitySensor.alertListeners(); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DoubleTapHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DoubleTapHelperTest.java new file mode 100644 index 000000000000..df1233af6406 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/DoubleTapHelperTest.java @@ -0,0 +1,369 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.phone; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.ArgumentMatchers.anyFloat; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.reset; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.content.res.Resources; +import android.os.SystemClock; +import android.testing.AndroidTestingRunner; +import android.view.MotionEvent; +import android.view.View; +import android.view.ViewConfiguration; + +import androidx.test.filters.SmallTest; + +import com.android.systemui.R; +import com.android.systemui.SysuiTestCase; + +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; + +@SmallTest +@RunWith(AndroidTestingRunner.class) +public class DoubleTapHelperTest extends SysuiTestCase { + + private DoubleTapHelper mDoubleTapHelper; + private int mTouchSlop; + private int mDoubleTouchSlop; + @Mock private View mView; + @Mock private DoubleTapHelper.ActivationListener mActivationListener; + @Mock private DoubleTapHelper.DoubleTapListener mDoubleTapListener; + @Mock private DoubleTapHelper.SlideBackListener mSlideBackListener; + @Mock private DoubleTapHelper.DoubleTapLogListener mDoubleTapLogListener; + @Mock private Resources mResources; + + @Before + public void setup() { + MockitoAnnotations.initMocks(this); + mTouchSlop = ViewConfiguration.get(mContext).getScaledTouchSlop(); + // The double tap slop has to be less than the regular slop, otherwise it has no effect. + mDoubleTouchSlop = mTouchSlop - 1; + when(mView.getContext()).thenReturn(mContext); + when(mView.getResources()).thenReturn(mResources); + when(mResources.getDimension(R.dimen.double_tap_slop)) + .thenReturn((float) mDoubleTouchSlop); + + mDoubleTapHelper = new DoubleTapHelper(mView, + mActivationListener, + mDoubleTapListener, + mSlideBackListener, mDoubleTapLogListener); + } + + @Test + public void testDoubleTap_success() { + long downtimeA = SystemClock.uptimeMillis(); + long downtimeB = downtimeA + 100; + + MotionEvent evDownA = MotionEvent.obtain(downtimeA, + downtimeA, + MotionEvent.ACTION_DOWN, + 1, + 1, + 0); + MotionEvent evUpA = MotionEvent.obtain(downtimeA, + downtimeA + 1, + MotionEvent.ACTION_UP, + 1, + 1, + 0); + MotionEvent evDownB = MotionEvent.obtain(downtimeB, + downtimeB, + MotionEvent.ACTION_DOWN, + 1, + 1, + 0); + MotionEvent evUpB = MotionEvent.obtain(downtimeB, + downtimeB + 1, + MotionEvent.ACTION_UP, + 1, + 1, + 0); + + mDoubleTapHelper.onTouchEvent(evDownA); + mDoubleTapHelper.onTouchEvent(evUpA); + verify(mActivationListener).onActiveChanged(true); + verify(mView).postDelayed(any(Runnable.class), anyLong()); + verify(mDoubleTapLogListener, never()).onDoubleTapLog(anyBoolean(), anyFloat(), anyFloat()); + verify(mDoubleTapListener, never()).onDoubleTap(); + + mDoubleTapHelper.onTouchEvent(evDownB); + mDoubleTapHelper.onTouchEvent(evUpB); + verify(mDoubleTapLogListener).onDoubleTapLog(true, 0, 0); + verify(mDoubleTapListener).onDoubleTap(); + + evDownA.recycle(); + evUpA.recycle(); + evDownB.recycle(); + evUpB.recycle(); + } + + @Test + public void testSingleTap_timeout() { + long downtimeA = SystemClock.uptimeMillis(); + + MotionEvent evDownA = MotionEvent.obtain(downtimeA, + downtimeA, + MotionEvent.ACTION_DOWN, + 1, + 1, + 0); + MotionEvent evUpA = MotionEvent.obtain(downtimeA, + downtimeA + 1, + MotionEvent.ACTION_UP, + 1, + 1, + 0); + + ArgumentCaptor<Runnable> runnableCaptor = ArgumentCaptor.forClass(Runnable.class); + mDoubleTapHelper.onTouchEvent(evDownA); + mDoubleTapHelper.onTouchEvent(evUpA); + verify(mActivationListener).onActiveChanged(true); + verify(mView).postDelayed(runnableCaptor.capture(), anyLong()); + runnableCaptor.getValue().run(); + verify(mActivationListener).onActiveChanged(true); + + evDownA.recycle(); + evUpA.recycle(); + } + + @Test + public void testSingleTap_slop() { + long downtimeA = SystemClock.uptimeMillis(); + + MotionEvent evDownA = MotionEvent.obtain(downtimeA, + downtimeA, + MotionEvent.ACTION_DOWN, + 1, + 1, + 0); + MotionEvent evUpA = MotionEvent.obtain(downtimeA, + downtimeA + 1, + MotionEvent.ACTION_UP, + 1 + mTouchSlop, + 1, + 0); + + ArgumentCaptor<Runnable> runnableCaptor = ArgumentCaptor.forClass(Runnable.class); + mDoubleTapHelper.onTouchEvent(evDownA); + mDoubleTapHelper.onTouchEvent(evUpA); + verify(mActivationListener, never()).onActiveChanged(true); + verify(mDoubleTapListener, never()).onDoubleTap(); + + evDownA.recycle(); + evUpA.recycle(); + } + + @Test + public void testDoubleTap_slop() { + long downtimeA = SystemClock.uptimeMillis(); + long downtimeB = downtimeA + 100; + + MotionEvent evDownA = MotionEvent.obtain(downtimeA, + downtimeA, + MotionEvent.ACTION_DOWN, + 1, + 1, + 0); + MotionEvent evUpA = MotionEvent.obtain(downtimeA, + downtimeA + 1, + MotionEvent.ACTION_UP, + 1, + 1, + 0); + MotionEvent evDownB = MotionEvent.obtain(downtimeB, + downtimeB, + MotionEvent.ACTION_DOWN, + 1, + 1, + 0); + MotionEvent evUpB = MotionEvent.obtain(downtimeB, + downtimeB + 1, + MotionEvent.ACTION_UP, + 1, + 1 + mDoubleTouchSlop, + 0); + + mDoubleTapHelper.onTouchEvent(evDownA); + mDoubleTapHelper.onTouchEvent(evUpA); + verify(mActivationListener).onActiveChanged(true); + verify(mView).postDelayed(any(Runnable.class), anyLong()); + + mDoubleTapHelper.onTouchEvent(evDownB); + mDoubleTapHelper.onTouchEvent(evUpB); + verify(mDoubleTapLogListener).onDoubleTapLog(false, 0, mDoubleTouchSlop); + verify(mActivationListener).onActiveChanged(false); + verify(mDoubleTapListener, never()).onDoubleTap(); + + evDownA.recycle(); + evUpA.recycle(); + evDownB.recycle(); + evUpB.recycle(); + } + + @Test + public void testSlideBack() { + long downtimeA = SystemClock.uptimeMillis(); + long downtimeB = downtimeA + 100; + + MotionEvent evDownA = MotionEvent.obtain(downtimeA, + downtimeA, + MotionEvent.ACTION_DOWN, + 1, + 1, + 0); + MotionEvent evUpA = MotionEvent.obtain(downtimeA, + downtimeA + 1, + MotionEvent.ACTION_UP, + 1, + 1, + 0); + MotionEvent evDownB = MotionEvent.obtain(downtimeB, + downtimeB, + MotionEvent.ACTION_DOWN, + 1, + 1, + 0); + MotionEvent evUpB = MotionEvent.obtain(downtimeB, + downtimeB + 1, + MotionEvent.ACTION_UP, + 1, + 1, + 0); + + when(mSlideBackListener.onSlideBack()).thenReturn(true); + + mDoubleTapHelper.onTouchEvent(evDownA); + mDoubleTapHelper.onTouchEvent(evUpA); + verify(mActivationListener, never()).onActiveChanged(true); + verify(mActivationListener, never()).onActiveChanged(false); + verify(mDoubleTapListener, never()).onDoubleTap(); + mDoubleTapHelper.onTouchEvent(evDownB); + mDoubleTapHelper.onTouchEvent(evUpB); + verify(mActivationListener, never()).onActiveChanged(true); + verify(mActivationListener, never()).onActiveChanged(false); + verify(mDoubleTapListener, never()).onDoubleTap(); + + evDownA.recycle(); + evUpA.recycle(); + evDownB.recycle(); + evUpB.recycle(); + } + + + @Test + public void testMoreThanTwoTaps() { + long downtimeA = SystemClock.uptimeMillis(); + long downtimeB = downtimeA + 100; + long downtimeC = downtimeB + 100; + long downtimeD = downtimeC + 100; + + MotionEvent evDownA = MotionEvent.obtain(downtimeA, + downtimeA, + MotionEvent.ACTION_DOWN, + 1, + 1, + 0); + MotionEvent evUpA = MotionEvent.obtain(downtimeA, + downtimeA + 1, + MotionEvent.ACTION_UP, + 1, + 1, + 0); + MotionEvent evDownB = MotionEvent.obtain(downtimeB, + downtimeB, + MotionEvent.ACTION_DOWN, + 1, + 1, + 0); + MotionEvent evUpB = MotionEvent.obtain(downtimeB, + downtimeB + 1, + MotionEvent.ACTION_UP, + 1, + 1, + 0); + MotionEvent evDownC = MotionEvent.obtain(downtimeC, + downtimeC, + MotionEvent.ACTION_DOWN, + 1, + 1, + 0); + MotionEvent evUpC = MotionEvent.obtain(downtimeC, + downtimeC + 1, + MotionEvent.ACTION_UP, + 1, + 1, + 0); + MotionEvent evDownD = MotionEvent.obtain(downtimeD, + downtimeD, + MotionEvent.ACTION_DOWN, + 1, + 1, + 0); + MotionEvent evUpD = MotionEvent.obtain(downtimeD, + downtimeD + 1, + MotionEvent.ACTION_UP, + 1, + 1, + 0); + + mDoubleTapHelper.onTouchEvent(evDownA); + mDoubleTapHelper.onTouchEvent(evUpA); + verify(mActivationListener).onActiveChanged(true); + verify(mView).postDelayed(any(Runnable.class), anyLong()); + verify(mDoubleTapLogListener, never()).onDoubleTapLog(anyBoolean(), anyFloat(), anyFloat()); + verify(mDoubleTapListener, never()).onDoubleTap(); + + mDoubleTapHelper.onTouchEvent(evDownB); + mDoubleTapHelper.onTouchEvent(evUpB); + verify(mDoubleTapLogListener).onDoubleTapLog(true, 0, 0); + verify(mDoubleTapListener).onDoubleTap(); + + reset(mView); + reset(mActivationListener); + reset(mDoubleTapLogListener); + reset(mDoubleTapListener); + + mDoubleTapHelper.onTouchEvent(evDownC); + mDoubleTapHelper.onTouchEvent(evUpC); + verify(mActivationListener).onActiveChanged(true); + verify(mView).postDelayed(any(Runnable.class), anyLong()); + verify(mDoubleTapLogListener, never()).onDoubleTapLog(anyBoolean(), anyFloat(), anyFloat()); + verify(mDoubleTapListener, never()).onDoubleTap(); + + mDoubleTapHelper.onTouchEvent(evDownD); + mDoubleTapHelper.onTouchEvent(evUpD); + verify(mDoubleTapLogListener).onDoubleTapLog(true, 0, 0); + verify(mDoubleTapListener).onDoubleTap(); + + evDownA.recycle(); + evUpA.recycle(); + evDownB.recycle(); + evUpB.recycle(); + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java index 214e26a4a2b5..f3ff7ec68bb2 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java @@ -133,6 +133,20 @@ public class ScrimControllerTest extends SysuiTestCase { } @Test + public void transitionToOff() { + mScrimController.transitionTo(ScrimState.OFF); + mScrimController.finishAnimationsImmediately(); + + assertScrimAlpha(OPAQUE /* front */, + SEMI_TRANSPARENT /* back */, + TRANSPARENT /* bubble */); + + assertScrimTint(true /* front */, + true /* behind */, + false /* bubble */); + } + + @Test public void transitionToAod_withRegularWallpaper() { mScrimController.transitionTo(ScrimState.AOD); mScrimController.finishAnimationsImmediately(); diff --git a/proto/src/metrics_constants/metrics_constants.proto b/proto/src/metrics_constants/metrics_constants.proto index 5b826b1c551b..b0e401bdda8a 100644 --- a/proto/src/metrics_constants/metrics_constants.proto +++ b/proto/src/metrics_constants/metrics_constants.proto @@ -2584,15 +2584,15 @@ message MetricsEvent { // ACTION: Logged when trampoline activity finishes. // TIME: Indicates total time taken by trampoline activity to finish in MS. - PROVISIONING_TRAMPOLINE_ACTIVITY_TIME_MS = 523; + PROVISIONING_TRAMPOLINE_ACTIVITY_TIME_MS = 523 [deprecated=true]; // ACTION: Logged when encryption activity finishes. // TIME: Indicates total time taken by post encryption activity to finish in MS. - PROVISIONING_POST_ENCRYPTION_ACTIVITY_TIME_MS = 524; + PROVISIONING_POST_ENCRYPTION_ACTIVITY_TIME_MS = 524 [deprecated=true]; // ACTION: Logged when finalization activity finishes. // TIME: Indicates time taken by finalization activity to finish in MS. - PROVISIONING_FINALIZATION_ACTIVITY_TIME_MS = 525; + PROVISIONING_FINALIZATION_ACTIVITY_TIME_MS = 525 [deprecated=true]; // OPEN: Settings Support > Phone/Chat -> Disclaimer DIALOG_SUPPORT_DISCLAIMER = 526; diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java index 91269c7ab37e..e909e7a828c3 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java @@ -16,13 +16,6 @@ package com.android.server.accessibility; -import static android.accessibilityservice.AccessibilityService.SHOW_MODE_AUTO; -import static android.accessibilityservice.AccessibilityService.SHOW_MODE_HARD_KEYBOARD_ORIGINAL_VALUE; -import static android.accessibilityservice.AccessibilityService.SHOW_MODE_HARD_KEYBOARD_OVERRIDDEN; -import static android.accessibilityservice.AccessibilityService.SHOW_MODE_HIDDEN; -import static android.accessibilityservice.AccessibilityService.SHOW_MODE_IGNORE_HARD_KEYBOARD; -import static android.accessibilityservice.AccessibilityService.SHOW_MODE_MASK; - import static com.android.internal.util.FunctionalUtils.ignoreRemoteException; import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage; @@ -50,8 +43,6 @@ import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.content.pm.ServiceInfo; import android.database.ContentObserver; -import android.graphics.Point; -import android.graphics.Rect; import android.graphics.Region; import android.hardware.display.DisplayManager; import android.hardware.fingerprint.IFingerprintService; @@ -119,7 +110,6 @@ import java.io.PrintWriter; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; -import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; @@ -136,6 +126,7 @@ import java.util.function.Consumer; */ public class AccessibilityManagerService extends IAccessibilityManager.Stub implements AbstractAccessibilityServiceConnection.SystemSupport, + AccessibilityUserState.ServiceInfoChangeListener, AccessibilityWindowManager.AccessibilityEventSender, AccessibilitySecurityPolicy.AccessibilityUserManager { @@ -178,12 +169,6 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub private final SimpleStringSplitter mStringColonSplitter = new SimpleStringSplitter(COMPONENT_NAME_SEPARATOR); - private final Rect mTempRect = new Rect(); - - private final Rect mTempRect1 = new Rect(); - - private final Point mTempPoint = new Point(); - private final PackageManager mPackageManager; private final PowerManager mPowerManager; @@ -226,7 +211,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub private final RemoteCallbackList<IAccessibilityManagerClient> mGlobalClients = new RemoteCallbackList<>(); - private final SparseArray<UserState> mUserStates = new SparseArray<>(); + private final SparseArray<AccessibilityUserState> mUserStates = new SparseArray<>(); private final UiAutomationManager mUiAutomationManager = new UiAutomationManager(mLock); @@ -237,7 +222,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub private boolean mIsAccessibilityButtonShown; - private UserState getCurrentUserStateLocked() { + private AccessibilityUserState getCurrentUserStateLocked() { return getUserStateLocked(mCurrentUserId); } @@ -293,6 +278,11 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub return mIsAccessibilityButtonShown; } + @Override + public void onServiceInfoChangedLocked(AccessibilityUserState userState) { + scheduleNotifyClientsOfServicesStateChangeLocked(userState); + } + @Nullable public FingerprintGestureDispatcher getFingerprintGestureDispatcher() { return mFingerprintGestureDispatcher; @@ -307,40 +297,40 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub } } - private UserState getUserState(int userId) { + private AccessibilityUserState getUserState(int userId) { synchronized (mLock) { return getUserStateLocked(userId); } } - private UserState getUserStateLocked(int userId) { - UserState state = mUserStates.get(userId); + @NonNull + private AccessibilityUserState getUserStateLocked(int userId) { + AccessibilityUserState state = mUserStates.get(userId); if (state == null) { - state = new UserState(userId); + state = new AccessibilityUserState(userId, mContext, this); mUserStates.put(userId, state); } return state; } boolean getBindInstantServiceAllowed(int userId) { - final UserState userState = getUserState(userId); - if (userState == null) return false; - return userState.getBindInstantServiceAllowed(); + synchronized (mLock) { + final AccessibilityUserState userState = getUserStateLocked(userId); + return userState.getBindInstantServiceAllowedLocked(); + } } void setBindInstantServiceAllowed(int userId, boolean allowed) { - UserState userState; + mContext.enforceCallingOrSelfPermission( + Manifest.permission.MANAGE_BIND_INSTANT_SERVICE, + "setBindInstantServiceAllowed"); synchronized (mLock) { - userState = getUserState(userId); - if (userState == null) { - if (!allowed) { - return; - } - userState = new UserState(userId); - mUserStates.put(userId, userState); + final AccessibilityUserState userState = getUserStateLocked(userId); + if (allowed != userState.getBindInstantServiceAllowedLocked()) { + userState.setBindInstantServiceAllowedLocked(allowed); + onUserStateChangedLocked(userState); } } - userState.setBindInstantServiceAllowed(allowed); } private void registerBroadcastReceivers() { @@ -354,7 +344,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub return; } // We will update when the automation service dies. - UserState userState = getCurrentUserStateLocked(); + AccessibilityUserState userState = getCurrentUserStateLocked(); // We have to reload the installed services since some services may // have different attributes, resolve info (does not support equals), // etc. Remove them then to force reload. @@ -376,8 +366,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub if (userId != mCurrentUserId) { return; } - UserState userState = getUserStateLocked(userId); - boolean reboundAService = userState.mBindingServices.removeIf( + AccessibilityUserState userState = getUserStateLocked(userId); + boolean reboundAService = userState.getBindingServicesLocked().removeIf( component -> component != null && component.getPackageName().equals(packageName)); if (reboundAService) { @@ -395,14 +385,14 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub if (userId != mCurrentUserId) { return; } - UserState userState = getUserStateLocked(userId); + AccessibilityUserState userState = getUserStateLocked(userId); Iterator<ComponentName> it = userState.mEnabledServices.iterator(); while (it.hasNext()) { ComponentName comp = it.next(); String compPkg = comp.getPackageName(); if (compPkg.equals(packageName)) { it.remove(); - userState.mBindingServices.remove(comp); + userState.getBindingServicesLocked().remove(comp); // Update the enabled services setting. persistComponentNamesToSettingLocked( Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES, @@ -430,7 +420,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub if (userId != mCurrentUserId) { return false; } - UserState userState = getUserStateLocked(userId); + AccessibilityUserState userState = getUserStateLocked(userId); Iterator<ComponentName> it = userState.mEnabledServices.iterator(); while (it.hasNext()) { ComponentName comp = it.next(); @@ -441,7 +431,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub return true; } it.remove(); - userState.mBindingServices.remove(comp); + userState.getBindingServicesLocked().remove(comp); persistComponentNamesToSettingLocked( Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES, userState.mEnabledServices, userId); @@ -478,7 +468,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub } else if (Intent.ACTION_USER_PRESENT.equals(action)) { // We will update when the automation service dies. synchronized (mLock) { - UserState userState = getCurrentUserStateLocked(); + AccessibilityUserState userState = getCurrentUserStateLocked(); if (readConfigurationForUserStateLocked(userState)) { onUserStateChangedLocked(userState); } @@ -509,7 +499,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub // If the client is from a process that runs across users such as // the system UI or the system we add it to the global state that // is shared across users. - UserState userState = getUserStateLocked(resolvedUserId); + AccessibilityUserState userState = getUserStateLocked(resolvedUserId); Client client = new Client(callback, Binder.getCallingUid(), userState); if (mSecurityPolicy.isCallerInteractingAcrossUsers(userId)) { mGlobalClients.register(callback, client); @@ -517,7 +507,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub Slog.i(LOG_TAG, "Added global client for pid:" + Binder.getCallingPid()); } return IntPair.of( - userState.getClientState(), + getClientStateLocked(userState), client.mLastSentRelevantEventTypes); } else { userState.mUserClients.register(callback, client); @@ -529,7 +519,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub + " and userId:" + mCurrentUserId); } return IntPair.of( - (resolvedUserId == mCurrentUserId) ? userState.getClientState() : 0, + (resolvedUserId == mCurrentUserId) ? getClientStateLocked(userState) : 0, client.mLastSentRelevantEventTypes); } } @@ -644,7 +634,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub .resolveCallingUserIdEnforcingPermissionsLocked(userId); // The automation service can suppress other services. - final UserState userState = getUserStateLocked(resolvedUserId); + final AccessibilityUserState userState = getUserStateLocked(resolvedUserId); if (mUiAutomationManager.suppressingAccessibilityServicesLocked()) { return Collections.emptyList(); } @@ -754,15 +744,15 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub } synchronized (mLock) { // Set the temporary state. - UserState userState = getCurrentUserStateLocked(); + AccessibilityUserState userState = getCurrentUserStateLocked(); - userState.mIsTouchExplorationEnabled = touchExplorationEnabled; - userState.mIsDisplayMagnificationEnabled = false; - userState.mIsNavBarMagnificationEnabled = false; - userState.mIsAutoclickEnabled = false; + userState.setTouchExplorationEnabledLocked(touchExplorationEnabled); + userState.setDisplayMagnificationEnabledLocked(false); + userState.setNavBarMagnificationEnabledLocked(false); + userState.setAutoclickEnabledLocked(false); userState.mEnabledServices.clear(); userState.mEnabledServices.add(service); - userState.mBindingServices.clear(); + userState.getBindingServicesLocked().clear(); userState.mTouchExplorationGrantedServices.clear(); userState.mTouchExplorationGrantedServices.add(service); @@ -943,7 +933,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub } // Disconnect from services for the old user. - UserState oldUserState = getCurrentUserStateLocked(); + AccessibilityUserState oldUserState = getCurrentUserStateLocked(); oldUserState.onSwitchToAnotherUserLocked(); // Disable the local managers for the old user. @@ -960,7 +950,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub // The user changed. mCurrentUserId = userId; - UserState userState = getCurrentUserStateLocked(); + AccessibilityUserState userState = getCurrentUserStateLocked(); readConfigurationForUserStateLocked(userState); // Even if reading did not yield change, we have to update @@ -979,8 +969,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub private void announceNewUserIfNeeded() { synchronized (mLock) { - UserState userState = getCurrentUserStateLocked(); - if (userState.isHandlingAccessibilityEvents()) { + AccessibilityUserState userState = getCurrentUserStateLocked(); + if (userState.isHandlingAccessibilityEventsLocked()) { UserManager userManager = (UserManager) mContext.getSystemService( Context.USER_SERVICE); String message = mContext.getString(R.string.user_switched, @@ -997,7 +987,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub synchronized (mLock) { int parentUserId = mSecurityPolicy.resolveProfileParentLocked(userId); if (parentUserId == mCurrentUserId) { - UserState userState = getUserStateLocked(mCurrentUserId); + AccessibilityUserState userState = getUserStateLocked(mCurrentUserId); onUserStateChangedLocked(userState); } } @@ -1015,7 +1005,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub readComponentNamesFromStringLocked(oldSetting, mTempComponentNameSet, false); readComponentNamesFromStringLocked(newSetting, mTempComponentNameSet, true); - UserState userState = getUserStateLocked(UserHandle.USER_SYSTEM); + AccessibilityUserState userState = getUserStateLocked(UserHandle.USER_SYSTEM); userState.mEnabledServices.clear(); userState.mEnabledServices.addAll(mTempComponentNameSet); persistComponentNamesToSettingLocked( @@ -1025,6 +1015,10 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub onUserStateChangedLocked(userState); } + private int getClientStateLocked(AccessibilityUserState userState) { + return userState.getClientStateLocked(mUiAutomationManager.isUiAutomationRunningLocked()); + } + private InteractionBridge getInteractionBridge() { synchronized (mLock) { if (mInteractionBridge == null) { @@ -1044,7 +1038,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub // gestures to avoid user frustration when different // behavior is observed from different combinations of // enabled accessibility services. - UserState state = getCurrentUserStateLocked(); + AccessibilityUserState state = getCurrentUserStateLocked(); for (int i = state.mBoundServices.size() - 1; i >= 0; i--) { AccessibilityServiceConnection service = state.mBoundServices.get(i); if (service.mRequestTouchExplorationMode && service.mIsDefault == isDefault) { @@ -1056,7 +1050,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub } private void notifyClearAccessibilityCacheLocked() { - UserState state = getCurrentUserStateLocked(); + AccessibilityUserState state = getCurrentUserStateLocked(); for (int i = state.mBoundServices.size() - 1; i >= 0; i--) { AccessibilityServiceConnection service = state.mBoundServices.get(i); service.notifyClearAccessibilityNodeInfoCache(); @@ -1065,25 +1059,17 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub private void notifyMagnificationChangedLocked(int displayId, @NonNull Region region, float scale, float centerX, float centerY) { - final UserState state = getCurrentUserStateLocked(); + final AccessibilityUserState state = getCurrentUserStateLocked(); for (int i = state.mBoundServices.size() - 1; i >= 0; i--) { final AccessibilityServiceConnection service = state.mBoundServices.get(i); service.notifyMagnificationChangedLocked(displayId, region, scale, centerX, centerY); } } - private void notifySoftKeyboardShowModeChangedLocked(int showMode) { - final UserState state = getCurrentUserStateLocked(); - for (int i = state.mBoundServices.size() - 1; i >= 0; i--) { - final AccessibilityServiceConnection service = state.mBoundServices.get(i); - service.notifySoftKeyboardShowModeChangedLocked(showMode); - } - } - private void notifyAccessibilityButtonClickedLocked(int displayId) { - final UserState state = getCurrentUserStateLocked(); + final AccessibilityUserState state = getCurrentUserStateLocked(); - int potentialTargets = state.mIsNavBarMagnificationEnabled ? 1 : 0; + int potentialTargets = state.isNavBarMagnificationEnabledLocked() ? 1 : 0; for (int i = state.mBoundServices.size() - 1; i >= 0; i--) { final AccessibilityServiceConnection service = state.mBoundServices.get(i); if (service.mRequestAccessibilityButton) { @@ -1095,7 +1081,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub return; } if (potentialTargets == 1) { - if (state.mIsNavBarMagnificationEnabled) { + if (state.isNavBarMagnificationEnabledLocked()) { mMainHandler.sendMessage(obtainMessage( AccessibilityManagerService::sendAccessibilityButtonToInputFilter, this, displayId)); @@ -1110,13 +1096,13 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub } } } else { - if (state.mServiceAssignedToAccessibilityButton == null - && !state.mIsNavBarMagnificationAssignedToAccessibilityButton) { + if (state.getServiceAssignedToAccessibilityButtonLocked() == null + && !state.isNavBarMagnificationAssignedToAccessibilityButtonLocked()) { mMainHandler.sendMessage(obtainMessage( AccessibilityManagerService::showAccessibilityButtonTargetSelection, this, displayId)); - } else if (state.mIsNavBarMagnificationEnabled - && state.mIsNavBarMagnificationAssignedToAccessibilityButton) { + } else if (state.isNavBarMagnificationEnabledLocked() + && state.isNavBarMagnificationAssignedToAccessibilityButtonLocked()) { mMainHandler.sendMessage(obtainMessage( AccessibilityManagerService::sendAccessibilityButtonToInputFilter, this, displayId)); @@ -1125,7 +1111,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub for (int i = state.mBoundServices.size() - 1; i >= 0; i--) { final AccessibilityServiceConnection service = state.mBoundServices.get(i); if (service.mRequestAccessibilityButton && (service.mComponentName.equals( - state.mServiceAssignedToAccessibilityButton))) { + state.getServiceAssignedToAccessibilityButtonLocked()))) { service.notifyAccessibilityButtonClickedLocked(displayId); return; } @@ -1154,7 +1140,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub } private void notifyAccessibilityButtonVisibilityChangedLocked(boolean available) { - final UserState state = getCurrentUserStateLocked(); + final AccessibilityUserState state = getCurrentUserStateLocked(); mIsAccessibilityButtonShown = available; for (int i = state.mBoundServices.size() - 1; i >= 0; i--) { final AccessibilityServiceConnection clientConnection = state.mBoundServices.get(i); @@ -1165,7 +1151,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub } } - private boolean readInstalledAccessibilityServiceLocked(UserState userState) { + private boolean readInstalledAccessibilityServiceLocked(AccessibilityUserState userState) { mTempAccessibilityServiceInfoList.clear(); int flags = PackageManager.GET_SERVICES @@ -1174,7 +1160,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub | PackageManager.MATCH_DIRECT_BOOT_AWARE | PackageManager.MATCH_DIRECT_BOOT_UNAWARE; - if (userState.getBindInstantServiceAllowed()) { + if (userState.getBindInstantServiceAllowedLocked()) { flags |= PackageManager.MATCH_INSTANT; } @@ -1209,7 +1195,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub return false; } - private boolean readInstalledAccessibilityShortcutLocked(UserState userState) { + private boolean readInstalledAccessibilityShortcutLocked(AccessibilityUserState userState) { final List<AccessibilityShortcutInfo> shortcutInfos = AccessibilityManager .getInstance(mContext).getInstalledAccessibilityShortcutListAsUser( mContext, mCurrentUserId); @@ -1221,7 +1207,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub return false; } - private boolean readEnabledAccessibilityServicesLocked(UserState userState) { + private boolean readEnabledAccessibilityServicesLocked(AccessibilityUserState userState) { mTempComponentNameSet.clear(); readComponentNamesFromSettingLocked(Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES, userState.mUserId, mTempComponentNameSet); @@ -1236,7 +1222,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub } private boolean readTouchExplorationGrantedAccessibilityServicesLocked( - UserState userState) { + AccessibilityUserState userState) { mTempComponentNameSet.clear(); readComponentNamesFromSettingLocked( Settings.Secure.TOUCH_EXPLORATION_GRANTED_ACCESSIBILITY_SERVICES, @@ -1261,7 +1247,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub private void notifyAccessibilityServicesDelayedLocked(AccessibilityEvent event, boolean isDefault) { try { - UserState state = getCurrentUserStateLocked(); + AccessibilityUserState state = getCurrentUserStateLocked(); for (int i = 0, count = state.mBoundServices.size(); i < count; i++) { AccessibilityServiceConnection service = state.mBoundServices.get(i); @@ -1276,7 +1262,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub } } - private void updateRelevantEventsLocked(UserState userState) { + private void updateRelevantEventsLocked(AccessibilityUserState userState) { mMainHandler.post(() -> { broadcastToClients(userState, ignoreRemoteException(client -> { int relevantEventTypes; @@ -1296,7 +1282,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub }); } - private int computeRelevantEventTypesLocked(UserState userState, Client client) { + private int computeRelevantEventTypesLocked(AccessibilityUserState userState, Client client) { int relevantEventTypes = 0; int serviceCount = userState.mBoundServices.size(); @@ -1342,20 +1328,11 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub } private void broadcastToClients( - UserState userState, Consumer<Client> clientAction) { + AccessibilityUserState userState, Consumer<Client> clientAction) { mGlobalClients.broadcastForEachCookie(clientAction); userState.mUserClients.broadcastForEachCookie(clientAction); } - private void unbindAllServicesLocked(UserState userState) { - List<AccessibilityServiceConnection> services = userState.mBoundServices; - for (int count = services.size(); count > 0; count--) { - // When the service is unbound, it disappears from the list, so there's no need to - // keep track of the index - services.get(0).unbindLocked(); - } - } - /** * Populates a set with the {@link ComponentName}s stored in a colon * separated value setting for a given user. @@ -1422,7 +1399,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub } } - private void updateServicesLocked(UserState userState) { + private void updateServicesLocked(AccessibilityUserState userState) { Map<ComponentName, AccessibilityServiceConnection> componentNameToServiceMap = userState.mComponentNameToServiceMap; boolean isUnlockingOrUnlocked = LocalServices.getService(UserManagerInternal.class) @@ -1442,7 +1419,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub } // Wait for the binding if it is in process. - if (userState.mBindingServices.contains(componentName)) { + if (userState.getBindingServicesLocked().contains(componentName)) { continue; } if (userState.mEnabledServices.contains(componentName) @@ -1478,15 +1455,15 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub if (audioManager != null) { audioManager.setAccessibilityServiceUids(mTempIntArray); } - updateAccessibilityEnabledSetting(userState); + updateAccessibilityEnabledSettingLocked(userState); } - private void scheduleUpdateClientsIfNeededLocked(UserState userState) { - final int clientState = userState.getClientState(); - if (userState.mLastSentClientState != clientState + private void scheduleUpdateClientsIfNeededLocked(AccessibilityUserState userState) { + final int clientState = getClientStateLocked(userState); + if (userState.getLastSentClientStateLocked() != clientState && (mGlobalClients.getRegisteredCallbackCount() > 0 || userState.mUserClients.getRegisteredCallbackCount() > 0)) { - userState.mLastSentClientState = clientState; + userState.setLastSentClientStateLocked(clientState); mMainHandler.sendMessage(obtainMessage( AccessibilityManagerService::sendStateToAllClients, this, clientState, userState.mUserId)); @@ -1508,7 +1485,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub client -> client.setState(clientState))); } - private void scheduleNotifyClientsOfServicesStateChangeLocked(UserState userState) { + private void scheduleNotifyClientsOfServicesStateChangeLocked( + AccessibilityUserState userState) { updateRecommendedUiTimeoutLocked(userState); mMainHandler.sendMessage(obtainMessage( AccessibilityManagerService::sendServicesStateChanged, @@ -1527,44 +1505,45 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub client -> client.notifyServicesStateChanged(uiTimeout))); } - private void scheduleUpdateInputFilter(UserState userState) { + private void scheduleUpdateInputFilter(AccessibilityUserState userState) { mMainHandler.sendMessage(obtainMessage( AccessibilityManagerService::updateInputFilter, this, userState)); } - private void scheduleUpdateFingerprintGestureHandling(UserState userState) { + private void scheduleUpdateFingerprintGestureHandling(AccessibilityUserState userState) { mMainHandler.sendMessage(obtainMessage( AccessibilityManagerService::updateFingerprintGestureHandling, this, userState)); } - private void updateInputFilter(UserState userState) { + private void updateInputFilter(AccessibilityUserState userState) { if (mUiAutomationManager.suppressingAccessibilityServicesLocked()) return; boolean setInputFilter = false; AccessibilityInputFilter inputFilter = null; synchronized (mLock) { int flags = 0; - if (userState.mIsDisplayMagnificationEnabled) { + if (userState.isDisplayMagnificationEnabledLocked()) { flags |= AccessibilityInputFilter.FLAG_FEATURE_SCREEN_MAGNIFIER; } - if (userState.mIsNavBarMagnificationEnabled) { + if (userState.isNavBarMagnificationEnabledLocked()) { flags |= AccessibilityInputFilter.FLAG_FEATURE_TRIGGERED_SCREEN_MAGNIFIER; } if (userHasMagnificationServicesLocked(userState)) { flags |= AccessibilityInputFilter.FLAG_FEATURE_CONTROL_SCREEN_MAGNIFIER; } // Touch exploration without accessibility makes no sense. - if (userState.isHandlingAccessibilityEvents() && userState.mIsTouchExplorationEnabled) { + if (userState.isHandlingAccessibilityEventsLocked() + && userState.isTouchExplorationEnabledLocked()) { flags |= AccessibilityInputFilter.FLAG_FEATURE_TOUCH_EXPLORATION; } - if (userState.mIsFilterKeyEventsEnabled) { + if (userState.isFilterKeyEventsEnabledLocked()) { flags |= AccessibilityInputFilter.FLAG_FEATURE_FILTER_KEY_EVENTS; } - if (userState.mIsAutoclickEnabled) { + if (userState.isAutoclickEnabledLocked()) { flags |= AccessibilityInputFilter.FLAG_FEATURE_AUTOCLICK; } - if (userState.mIsPerformGesturesEnabled) { + if (userState.isPerformGesturesEnabledLocked()) { flags |= AccessibilityInputFilter.FLAG_FEATURE_INJECT_MOTION_EVENTS; } if (flags != 0) { @@ -1597,8 +1576,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub String label = service.getServiceInfo().getResolveInfo() .loadLabel(mContext.getPackageManager()).toString(); - final UserState userState = getCurrentUserStateLocked(); - if (userState.mIsTouchExplorationEnabled) { + final AccessibilityUserState userState = getCurrentUserStateLocked(); + if (userState.isTouchExplorationEnabledLocked()) { return; } if (mEnableTouchExplorationDialog != null @@ -1608,40 +1587,40 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub mEnableTouchExplorationDialog = new AlertDialog.Builder(mContext) .setIconAttribute(android.R.attr.alertDialogIcon) .setPositiveButton(android.R.string.ok, new OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - // The user allowed the service to toggle touch exploration. - userState.mTouchExplorationGrantedServices.add(service.mComponentName); - persistComponentNamesToSettingLocked( - Settings.Secure.TOUCH_EXPLORATION_GRANTED_ACCESSIBILITY_SERVICES, - userState.mTouchExplorationGrantedServices, userState.mUserId); - // Enable touch exploration. - userState.mIsTouchExplorationEnabled = true; - final long identity = Binder.clearCallingIdentity(); - try { - Settings.Secure.putIntForUser(mContext.getContentResolver(), - Settings.Secure.TOUCH_EXPLORATION_ENABLED, 1, - userState.mUserId); - } finally { - Binder.restoreCallingIdentity(identity); - } - onUserStateChangedLocked(userState); - } - }) - .setNegativeButton(android.R.string.cancel, new OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - dialog.dismiss(); - } - }) - .setTitle(R.string.enable_explore_by_touch_warning_title) - .setMessage(mContext.getString( - R.string.enable_explore_by_touch_warning_message, label)) - .create(); + @Override + public void onClick(DialogInterface dialog, int which) { + // The user allowed the service to toggle touch exploration. + userState.mTouchExplorationGrantedServices.add(service.mComponentName); + persistComponentNamesToSettingLocked( + Settings.Secure.TOUCH_EXPLORATION_GRANTED_ACCESSIBILITY_SERVICES, + userState.mTouchExplorationGrantedServices, userState.mUserId); + // Enable touch exploration. + userState.setTouchExplorationEnabledLocked(true); + final long identity = Binder.clearCallingIdentity(); + try { + Settings.Secure.putIntForUser(mContext.getContentResolver(), + Settings.Secure.TOUCH_EXPLORATION_ENABLED, 1, + userState.mUserId); + } finally { + Binder.restoreCallingIdentity(identity); + } + onUserStateChangedLocked(userState); + } + }) + .setNegativeButton(android.R.string.cancel, new OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + dialog.dismiss(); + } + }) + .setTitle(R.string.enable_explore_by_touch_warning_title) + .setMessage(mContext.getString( + R.string.enable_explore_by_touch_warning_message, label)) + .create(); mEnableTouchExplorationDialog.getWindow().setType( WindowManager.LayoutParams.TYPE_SYSTEM_ALERT); mEnableTouchExplorationDialog.getWindow().getAttributes().privateFlags - |= WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS; + |= WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS; mEnableTouchExplorationDialog.setCanceledOnTouchOutside(true); mEnableTouchExplorationDialog.show(); } @@ -1652,7 +1631,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub * * @param userState the new user state */ - private void onUserStateChangedLocked(UserState userState) { + private void onUserStateChangedLocked(AccessibilityUserState userState) { // TODO: Remove this hack mInitialized = true; updateLegacyCapabilitiesLocked(userState); @@ -1670,7 +1649,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub updateAccessibilityButtonTargetsLocked(userState); } - private void updateWindowsForAccessibilityCallbackLocked(UserState userState) { + private void updateWindowsForAccessibilityCallbackLocked(AccessibilityUserState userState) { // We observe windows for accessibility only if there is at least // one bound service that can retrieve window content that specified // it is interested in accessing such windows. For services that are @@ -1702,7 +1681,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub } } - private void updateLegacyCapabilitiesLocked(UserState userState) { + private void updateLegacyCapabilitiesLocked(AccessibilityUserState userState) { // Up to JB-MR1 we had a white list with services that can enable touch // exploration. When a service is first started we show a dialog to the // use to get a permission to white list the service. @@ -1724,20 +1703,20 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub } } - private void updatePerformGesturesLocked(UserState userState) { + private void updatePerformGesturesLocked(AccessibilityUserState userState) { final int serviceCount = userState.mBoundServices.size(); for (int i = 0; i < serviceCount; i++) { AccessibilityServiceConnection service = userState.mBoundServices.get(i); if ((service.getCapabilities() & AccessibilityServiceInfo.CAPABILITY_CAN_PERFORM_GESTURES) != 0) { - userState.mIsPerformGesturesEnabled = true; + userState.setPerformGesturesEnabledLocked(true); return; } } - userState.mIsPerformGesturesEnabled = false; + userState.setPerformGesturesEnabledLocked(false); } - private void updateFilterKeyEventsLocked(UserState userState) { + private void updateFilterKeyEventsLocked(AccessibilityUserState userState) { final int serviceCount = userState.mBoundServices.size(); for (int i = 0; i < serviceCount; i++) { AccessibilityServiceConnection service = userState.mBoundServices.get(i); @@ -1745,14 +1724,14 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub && (service.getCapabilities() & AccessibilityServiceInfo .CAPABILITY_CAN_REQUEST_FILTER_KEY_EVENTS) != 0) { - userState.mIsFilterKeyEventsEnabled = true; + userState.setFilterKeyEventsEnabledLocked(true); return; } } - userState.mIsFilterKeyEventsEnabled = false; + userState.setFilterKeyEventsEnabledLocked(false); } - private boolean readConfigurationForUserStateLocked(UserState userState) { + private boolean readConfigurationForUserStateLocked(AccessibilityUserState userState) { boolean somethingChanged = readInstalledAccessibilityServiceLocked(userState); somethingChanged |= readInstalledAccessibilityShortcutLocked(userState); somethingChanged |= readEnabledAccessibilityServicesLocked(userState); @@ -1767,10 +1746,10 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub return somethingChanged; } - private void updateAccessibilityEnabledSetting(UserState userState) { + private void updateAccessibilityEnabledSettingLocked(AccessibilityUserState userState) { final long identity = Binder.clearCallingIdentity(); final boolean isA11yEnabled = mUiAutomationManager.isUiAutomationRunningLocked() - || userState.isHandlingAccessibilityEvents(); + || userState.isHandlingAccessibilityEventsLocked(); try { Settings.Secure.putIntForUser(mContext.getContentResolver(), Settings.Secure.ACCESSIBILITY_ENABLED, @@ -1781,18 +1760,18 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub } } - private boolean readTouchExplorationEnabledSettingLocked(UserState userState) { + private boolean readTouchExplorationEnabledSettingLocked(AccessibilityUserState userState) { final boolean touchExplorationEnabled = Settings.Secure.getIntForUser( mContext.getContentResolver(), Settings.Secure.TOUCH_EXPLORATION_ENABLED, 0, userState.mUserId) == 1; - if (touchExplorationEnabled != userState.mIsTouchExplorationEnabled) { - userState.mIsTouchExplorationEnabled = touchExplorationEnabled; + if (touchExplorationEnabled != userState.isTouchExplorationEnabledLocked()) { + userState.setTouchExplorationEnabledLocked(touchExplorationEnabled); return true; } return false; } - private boolean readMagnificationEnabledSettingsLocked(UserState userState) { + private boolean readMagnificationEnabledSettingsLocked(AccessibilityUserState userState) { final boolean displayMagnificationEnabled = Settings.Secure.getIntForUser( mContext.getContentResolver(), Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_ENABLED, @@ -1801,40 +1780,40 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub mContext.getContentResolver(), Settings.Secure.ACCESSIBILITY_DISPLAY_MAGNIFICATION_NAVBAR_ENABLED, 0, userState.mUserId) == 1; - if ((displayMagnificationEnabled != userState.mIsDisplayMagnificationEnabled) - || (navBarMagnificationEnabled != userState.mIsNavBarMagnificationEnabled)) { - userState.mIsDisplayMagnificationEnabled = displayMagnificationEnabled; - userState.mIsNavBarMagnificationEnabled = navBarMagnificationEnabled; + if ((displayMagnificationEnabled != userState.isDisplayMagnificationEnabledLocked()) + || (navBarMagnificationEnabled != userState.isNavBarMagnificationEnabledLocked())) { + userState.setDisplayMagnificationEnabledLocked(displayMagnificationEnabled); + userState.setNavBarMagnificationEnabledLocked(navBarMagnificationEnabled); return true; } return false; } - private boolean readAutoclickEnabledSettingLocked(UserState userState) { + private boolean readAutoclickEnabledSettingLocked(AccessibilityUserState userState) { final boolean autoclickEnabled = Settings.Secure.getIntForUser( mContext.getContentResolver(), Settings.Secure.ACCESSIBILITY_AUTOCLICK_ENABLED, 0, userState.mUserId) == 1; - if (autoclickEnabled != userState.mIsAutoclickEnabled) { - userState.mIsAutoclickEnabled = autoclickEnabled; + if (autoclickEnabled != userState.isAutoclickEnabledLocked()) { + userState.setAutoclickEnabledLocked(autoclickEnabled); return true; } return false; } - private boolean readHighTextContrastEnabledSettingLocked(UserState userState) { + private boolean readHighTextContrastEnabledSettingLocked(AccessibilityUserState userState) { final boolean highTextContrastEnabled = Settings.Secure.getIntForUser( mContext.getContentResolver(), Settings.Secure.ACCESSIBILITY_HIGH_TEXT_CONTRAST_ENABLED, 0, userState.mUserId) == 1; - if (highTextContrastEnabled != userState.mIsTextHighContrastEnabled) { - userState.mIsTextHighContrastEnabled = highTextContrastEnabled; + if (highTextContrastEnabled != userState.isTextHighContrastEnabledLocked()) { + userState.setTextHighContrastEnabledLocked(highTextContrastEnabled); return true; } return false; } - private void updateTouchExplorationLocked(UserState userState) { + private void updateTouchExplorationLocked(AccessibilityUserState userState) { boolean enabled = mUiAutomationManager.isTouchExplorationEnabledLocked(); final int serviceCount = userState.mBoundServices.size(); for (int i = 0; i < serviceCount; i++) { @@ -1844,8 +1823,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub break; } } - if (enabled != userState.mIsTouchExplorationEnabled) { - userState.mIsTouchExplorationEnabled = enabled; + if (enabled != userState.isTouchExplorationEnabledLocked()) { + userState.setTouchExplorationEnabledLocked(enabled); final long identity = Binder.clearCallingIdentity(); try { Settings.Secure.putIntForUser(mContext.getContentResolver(), @@ -1857,60 +1836,61 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub } } - private boolean readAccessibilityShortcutSettingLocked(UserState userState) { + private boolean readAccessibilityShortcutSettingLocked(AccessibilityUserState userState) { String componentNameToEnableString = AccessibilityShortcutController .getTargetServiceComponentNameString(mContext, userState.mUserId); if ((componentNameToEnableString == null) || componentNameToEnableString.isEmpty()) { - if (userState.mServiceToEnableWithShortcut == null) { + if (userState.getServiceToEnableWithShortcutLocked() == null) { return false; } - userState.mServiceToEnableWithShortcut = null; + userState.setServiceToEnableWithShortcutLocked(null); return true; } ComponentName componentNameToEnable = ComponentName.unflattenFromString(componentNameToEnableString); if ((componentNameToEnable != null) - && componentNameToEnable.equals(userState.mServiceToEnableWithShortcut)) { + && componentNameToEnable.equals(userState.getServiceToEnableWithShortcutLocked())) { return false; } - userState.mServiceToEnableWithShortcut = componentNameToEnable; + userState.setServiceToEnableWithShortcutLocked(componentNameToEnable); scheduleNotifyClientsOfServicesStateChangeLocked(userState); return true; } - private boolean readAccessibilityButtonSettingsLocked(UserState userState) { + private boolean readAccessibilityButtonSettingsLocked(AccessibilityUserState userState) { String componentId = Settings.Secure.getStringForUser(mContext.getContentResolver(), Settings.Secure.ACCESSIBILITY_BUTTON_TARGET_COMPONENT, userState.mUserId); if (TextUtils.isEmpty(componentId)) { - if ((userState.mServiceAssignedToAccessibilityButton == null) - && !userState.mIsNavBarMagnificationAssignedToAccessibilityButton) { + if ((userState.getServiceAssignedToAccessibilityButtonLocked() == null) + && !userState.isNavBarMagnificationAssignedToAccessibilityButtonLocked()) { return false; } - userState.mServiceAssignedToAccessibilityButton = null; - userState.mIsNavBarMagnificationAssignedToAccessibilityButton = false; + userState.setServiceAssignedToAccessibilityButtonLocked(null); + userState.setNavBarMagnificationAssignedToAccessibilityButtonLocked(false); return true; } if (componentId.equals(MagnificationController.class.getName())) { - if (userState.mIsNavBarMagnificationAssignedToAccessibilityButton) { + if (userState.isNavBarMagnificationAssignedToAccessibilityButtonLocked()) { return false; } - userState.mServiceAssignedToAccessibilityButton = null; - userState.mIsNavBarMagnificationAssignedToAccessibilityButton = true; + userState.setServiceAssignedToAccessibilityButtonLocked(null); + userState.setNavBarMagnificationAssignedToAccessibilityButtonLocked(true); return true; } ComponentName componentName = ComponentName.unflattenFromString(componentId); - if (Objects.equals(componentName, userState.mServiceAssignedToAccessibilityButton)) { + if (Objects.equals(componentName, + userState.getServiceAssignedToAccessibilityButtonLocked())) { return false; } - userState.mServiceAssignedToAccessibilityButton = componentName; - userState.mIsNavBarMagnificationAssignedToAccessibilityButton = false; + userState.setServiceAssignedToAccessibilityButtonLocked(componentName); + userState.setNavBarMagnificationAssignedToAccessibilityButtonLocked(false); return true; } - private boolean readUserRecommendedUiTimeoutSettingsLocked(UserState userState) { + private boolean readUserRecommendedUiTimeoutSettingsLocked(AccessibilityUserState userState) { final int nonInteractiveUiTimeout = Settings.Secure.getIntForUser( mContext.getContentResolver(), Settings.Secure.ACCESSIBILITY_NON_INTERACTIVE_UI_TIMEOUT_MS, 0, @@ -1919,10 +1899,10 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub mContext.getContentResolver(), Settings.Secure.ACCESSIBILITY_INTERACTIVE_UI_TIMEOUT_MS, 0, userState.mUserId); - if (nonInteractiveUiTimeout != userState.mUserNonInteractiveUiTimeout - || interactiveUiTimeout != userState.mUserInteractiveUiTimeout) { - userState.mUserNonInteractiveUiTimeout = nonInteractiveUiTimeout; - userState.mUserInteractiveUiTimeout = interactiveUiTimeout; + if (nonInteractiveUiTimeout != userState.getUserNonInteractiveUiTimeoutLocked() + || interactiveUiTimeout != userState.getUserInteractiveUiTimeoutLocked()) { + userState.setUserNonInteractiveUiTimeoutLocked(nonInteractiveUiTimeout); + userState.setUserInteractiveUiTimeoutLocked(interactiveUiTimeout); scheduleNotifyClientsOfServicesStateChangeLocked(userState); return true; } @@ -1936,22 +1916,22 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub * * @param userState */ - private void updateAccessibilityShortcutLocked(UserState userState) { - if (userState.mServiceToEnableWithShortcut == null) { + private void updateAccessibilityShortcutLocked(AccessibilityUserState userState) { + if (userState.getServiceToEnableWithShortcutLocked() == null) { return; } boolean shortcutServiceIsInstalled = AccessibilityShortcutController.getFrameworkShortcutFeaturesMap() - .containsKey(userState.mServiceToEnableWithShortcut); + .containsKey(userState.getServiceToEnableWithShortcutLocked()); for (int i = 0; !shortcutServiceIsInstalled && (i < userState.mInstalledServices.size()); i++) { if (userState.mInstalledServices.get(i).getComponentName() - .equals(userState.mServiceToEnableWithShortcut)) { + .equals(userState.getServiceToEnableWithShortcutLocked())) { shortcutServiceIsInstalled = true; } } if (!shortcutServiceIsInstalled) { - userState.mServiceToEnableWithShortcut = null; + userState.setServiceToEnableWithShortcutLocked(null); final long identity = Binder.clearCallingIdentity(); try { Settings.Secure.putStringForUser(mContext.getContentResolver(), @@ -1967,7 +1947,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub } private boolean canRequestAndRequestsTouchExplorationLocked( - AccessibilityServiceConnection service, UserState userState) { + AccessibilityServiceConnection service, AccessibilityUserState userState) { // Service not ready or cannot request the feature - well nothing to do. if (!service.canReceiveEventsLocked() || !service.mRequestTouchExplorationMode) { return false; @@ -1997,7 +1977,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub return false; } - private void updateMagnificationLocked(UserState userState) { + private void updateMagnificationLocked(AccessibilityUserState userState) { if (userState.mUserId != mCurrentUserId) { return; } @@ -2012,8 +1992,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub // We would skip overlay display because it uses overlay window to simulate secondary // displays in one display. It's not a real display and there's no input events for it. final ArrayList<Display> displays = getValidDisplayList(); - if (userState.mIsDisplayMagnificationEnabled - || userState.mIsNavBarMagnificationEnabled) { + if (userState.isDisplayMagnificationEnabledLocked() + || userState.isNavBarMagnificationEnabledLocked()) { for (int i = 0; i < displays.size(); i++) { final Display display = displays.get(i); getMagnificationController().register(display.getDisplayId()); @@ -2037,7 +2017,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub * Returns whether the specified user has any services that are capable of * controlling magnification. */ - private boolean userHasMagnificationServicesLocked(UserState userState) { + private boolean userHasMagnificationServicesLocked(AccessibilityUserState userState) { final List<AccessibilityServiceConnection> services = userState.mBoundServices; for (int i = 0, count = services.size(); i < count; i++) { final AccessibilityServiceConnection service = services.get(i); @@ -2052,7 +2032,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub * Returns whether the specified user has any services that are capable of * controlling magnification and are actively listening for magnification updates. */ - private boolean userHasListeningMagnificationServicesLocked(UserState userState, + private boolean userHasListeningMagnificationServicesLocked(AccessibilityUserState userState, int displayId) { final List<AccessibilityServiceConnection> services = userState.mBoundServices; for (int i = 0, count = services.size(); i < count; i++) { @@ -2065,7 +2045,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub return false; } - private void updateFingerprintGestureHandling(UserState userState) { + private void updateFingerprintGestureHandling(AccessibilityUserState userState) { final List<AccessibilityServiceConnection> services; synchronized (mLock) { services = userState.mBoundServices; @@ -2097,7 +2077,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub } } - private void updateAccessibilityButtonTargetsLocked(UserState userState) { + private void updateAccessibilityButtonTargetsLocked(AccessibilityUserState userState) { for (int i = userState.mBoundServices.size() - 1; i >= 0; i--) { final AccessibilityServiceConnection service = userState.mBoundServices.get(i); if (service.mRequestAccessibilityButton) { @@ -2107,9 +2087,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub } } - private void updateRecommendedUiTimeoutLocked(UserState userState) { - int newNonInteractiveUiTimeout = userState.mUserNonInteractiveUiTimeout; - int newInteractiveUiTimeout = userState.mUserInteractiveUiTimeout; + private void updateRecommendedUiTimeoutLocked(AccessibilityUserState userState) { + int newNonInteractiveUiTimeout = userState.getUserNonInteractiveUiTimeoutLocked(); + int newInteractiveUiTimeout = userState.getUserInteractiveUiTimeoutLocked(); // read from a11y services if user does not specify value if (newNonInteractiveUiTimeout == 0 || newInteractiveUiTimeout == 0) { int serviceNonInteractiveUiTimeout = 0; @@ -2132,8 +2112,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub newInteractiveUiTimeout = serviceInteractiveUiTimeout; } } - userState.mNonInteractiveUiTimeout = newNonInteractiveUiTimeout; - userState.mInteractiveUiTimeout = newInteractiveUiTimeout; + userState.setNonInteractiveUiTimeoutLocked(newNonInteractiveUiTimeout); + userState.setInteractiveUiTimeoutLocked(newInteractiveUiTimeout); } @GuardedBy("mLock") @@ -2180,8 +2160,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub final Map<ComponentName, ToggleableFrameworkFeatureInfo> frameworkFeatureMap = AccessibilityShortcutController.getFrameworkShortcutFeaturesMap(); synchronized(mLock) { - final UserState userState = getUserStateLocked(mCurrentUserId); - final ComponentName serviceName = userState.mServiceToEnableWithShortcut; + final AccessibilityUserState userState = getUserStateLocked(mCurrentUserId); + final ComponentName serviceName = userState.getServiceToEnableWithShortcutLocked(); if (serviceName == null) { return; } @@ -2218,11 +2198,11 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub "getAccessibilityShortcutService requires the MANAGE_ACCESSIBILITY permission"); } synchronized(mLock) { - final UserState userState = getUserStateLocked(mCurrentUserId); - if (userState.mServiceToEnableWithShortcut == null) { + final AccessibilityUserState userState = getUserStateLocked(mCurrentUserId); + if (userState.getServiceToEnableWithShortcutLocked() == null) { return null; } - return userState.mServiceToEnableWithShortcut.flattenToString(); + return userState.getServiceToEnableWithShortcutLocked().flattenToString(); } } @@ -2237,7 +2217,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub userId); setting.write(ComponentNameSet.add(setting.read(), componentName)); - UserState userState = getUserStateLocked(userId); + AccessibilityUserState userState = getUserStateLocked(userId); if (userState.mEnabledServices.add(componentName)) { onUserStateChangedLocked(userState); } @@ -2254,7 +2234,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub userId); setting.write(ComponentNameSet.remove(setting.read(), componentName)); - UserState userState = getUserStateLocked(userId); + AccessibilityUserState userState = getUserStateLocked(userId); if (userState.mEnabledServices.remove(componentName)) { onUserStateChangedLocked(userState); } @@ -2322,14 +2302,14 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub @Override public long getRecommendedTimeoutMillis() { synchronized(mLock) { - final UserState userState = getCurrentUserStateLocked(); + final AccessibilityUserState userState = getCurrentUserStateLocked(); return getRecommendedTimeoutMillisLocked(userState); } } - private long getRecommendedTimeoutMillisLocked(UserState userState) { - return IntPair.of(userState.mInteractiveUiTimeout, - userState.mNonInteractiveUiTimeout); + private long getRecommendedTimeoutMillisLocked(AccessibilityUserState userState) { + return IntPair.of(userState.getInteractiveUiTimeoutLocked(), + userState.getNonInteractiveUiTimeoutLocked()); } @Override @@ -2338,78 +2318,20 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub synchronized (mLock) { pw.println("ACCESSIBILITY MANAGER (dumpsys accessibility)"); pw.println(); + pw.append("currentUserId=").append(String.valueOf(mCurrentUserId)); + pw.println(); final int userCount = mUserStates.size(); for (int i = 0; i < userCount; i++) { - UserState userState = mUserStates.valueAt(i); - pw.append("User state[attributes:{id=" + userState.mUserId); - pw.append(", currentUser=" + (userState.mUserId == mCurrentUserId)); - pw.append(", touchExplorationEnabled=" + userState.mIsTouchExplorationEnabled); - pw.append(", displayMagnificationEnabled=" - + userState.mIsDisplayMagnificationEnabled); - pw.append(", navBarMagnificationEnabled=" - + userState.mIsNavBarMagnificationEnabled); - pw.append(", autoclickEnabled=" + userState.mIsAutoclickEnabled); - pw.append(", nonInteractiveUiTimeout=" + userState.mNonInteractiveUiTimeout); - pw.append(", interactiveUiTimeout=" + userState.mInteractiveUiTimeout); - pw.append(", installedServiceCount=" + userState.mInstalledServices.size()); - if (mUiAutomationManager.isUiAutomationRunningLocked()) { - pw.append(", "); - mUiAutomationManager.dumpUiAutomationService(fd, pw, args); - pw.println(); - } - pw.append("}"); - pw.println(); - pw.append(" Bound services:{"); - final int serviceCount = userState.mBoundServices.size(); - for (int j = 0; j < serviceCount; j++) { - if (j > 0) { - pw.append(", "); - pw.println(); - pw.append(" "); - } - AccessibilityServiceConnection service = userState.mBoundServices.get(j); - service.dump(fd, pw, args); - } - pw.println("}"); - pw.append(" Enabled services:{"); - Iterator<ComponentName> it = userState.mEnabledServices.iterator(); - if (it.hasNext()) { - ComponentName componentName = it.next(); - pw.append(componentName.toShortString()); - while (it.hasNext()) { - componentName = it.next(); - pw.append(", "); - pw.append(componentName.toShortString()); - } - } - pw.println("}"); - pw.append(" Binding services:{"); - it = userState.mBindingServices.iterator(); - if (it.hasNext()) { - ComponentName componentName = it.next(); - pw.append(componentName.toShortString()); - while (it.hasNext()) { - componentName = it.next(); - pw.append(", "); - pw.append(componentName.toShortString()); - } - } - pw.println("}]"); + mUserStates.valueAt(i).dump(fd, pw, args); + } + if (mUiAutomationManager.isUiAutomationRunningLocked()) { + mUiAutomationManager.dumpUiAutomationService(fd, pw, args); pw.println(); } mA11yWindowManager.dump(fd, pw, args); } } - private void putSecureIntForUser(String key, int value, int userid) { - final long identity = Binder.clearCallingIdentity(); - try { - Settings.Secure.putIntForUser(mContext.getContentResolver(), key, value, userid); - } finally { - Binder.restoreCallingIdentity(identity); - } - } - //TODO remove after refactoring KeyEventDispatcherTest final class MainHandler extends Handler { public static final int MSG_SEND_KEY_EVENT_TO_INPUT_FILTER = 8; @@ -2446,7 +2368,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub @Override public void onClientChangeLocked(boolean serviceInfoChanged) { - AccessibilityManagerService.UserState userState = getUserStateLocked(mCurrentUserId); + AccessibilityUserState userState = getUserStateLocked(mCurrentUserId); onUserStateChangedLocked(userState); if (serviceInfoChanged) { scheduleNotifyClientsOfServicesStateChangeLocked(userState); @@ -2474,7 +2396,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub info.setCapabilities(AccessibilityServiceInfo.CAPABILITY_CAN_RETRIEVE_WINDOW_CONTENT); info.flags |= AccessibilityServiceInfo.FLAG_RETRIEVE_INTERACTIVE_WINDOWS; info.flags |= AccessibilityServiceInfo.FLAG_INCLUDE_NOT_IMPORTANT_VIEWS; - final UserState userState; + final AccessibilityUserState userState; synchronized (mLock) { userState = getCurrentUserStateLocked(); } @@ -2593,7 +2515,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub if (mInputFilter != null) { mInputFilter.onDisplayChanged(); } - UserState userState = getCurrentUserStateLocked(); + AccessibilityUserState userState = getCurrentUserStateLocked(); if (displayId != Display.DEFAULT_DISPLAY) { final List<AccessibilityServiceConnection> services = userState.mBoundServices; for (int i = 0; i < services.size(); i++) { @@ -2618,7 +2540,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub if (mInputFilter != null) { mInputFilter.onDisplayChanged(); } - UserState userState = getCurrentUserStateLocked(); + AccessibilityUserState userState = getCurrentUserStateLocked(); if (displayId != Display.DEFAULT_DISPLAY) { final List<AccessibilityServiceConnection> services = userState.mBoundServices; for (int i = 0; i < services.size(); i++) { @@ -2658,7 +2580,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub final String[] mPackageNames; int mLastSentRelevantEventTypes; - private Client(IAccessibilityManagerClient callback, int clientUid, UserState userState) { + private Client(IAccessibilityManagerClient callback, int clientUid, + AccessibilityUserState userState) { mCallback = callback; mPackageNames = mPackageManager.getPackagesForUid(clientUid); synchronized (mLock) { @@ -2667,316 +2590,6 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub } } - public class UserState { - public final int mUserId; - - // Non-transient state. - - public final RemoteCallbackList<IAccessibilityManagerClient> mUserClients = - new RemoteCallbackList<>(); - - // Transient state. - - public final ArrayList<AccessibilityServiceConnection> mBoundServices = new ArrayList<>(); - - public final Map<ComponentName, AccessibilityServiceConnection> mComponentNameToServiceMap = - new HashMap<>(); - - public final List<AccessibilityServiceInfo> mInstalledServices = - new ArrayList<>(); - - public final List<AccessibilityShortcutInfo> mInstalledShortcuts = new ArrayList<>(); - - private final Set<ComponentName> mBindingServices = new HashSet<>(); - - public final Set<ComponentName> mEnabledServices = new HashSet<>(); - - public final Set<ComponentName> mTouchExplorationGrantedServices = - new HashSet<>(); - - public ComponentName mServiceChangingSoftKeyboardMode; - - public ComponentName mServiceToEnableWithShortcut; - - public int mLastSentClientState = -1; - public int mNonInteractiveUiTimeout = 0; - public int mInteractiveUiTimeout = 0; - - private int mSoftKeyboardShowMode = 0; - - public boolean mIsNavBarMagnificationAssignedToAccessibilityButton; - public ComponentName mServiceAssignedToAccessibilityButton; - - public boolean mIsTouchExplorationEnabled; - public boolean mIsTextHighContrastEnabled; - public boolean mIsDisplayMagnificationEnabled; - public boolean mIsNavBarMagnificationEnabled; - public boolean mIsAutoclickEnabled; - public boolean mIsPerformGesturesEnabled; - public boolean mIsFilterKeyEventsEnabled; - public int mUserNonInteractiveUiTimeout; - public int mUserInteractiveUiTimeout; - - private boolean mBindInstantServiceAllowed; - - public UserState(int userId) { - mUserId = userId; - } - - public int getClientState() { - int clientState = 0; - final boolean a11yEnabled = (mUiAutomationManager.isUiAutomationRunningLocked() - || isHandlingAccessibilityEvents()); - if (a11yEnabled) { - clientState |= AccessibilityManager.STATE_FLAG_ACCESSIBILITY_ENABLED; - } - // Touch exploration relies on enabled accessibility. - if (a11yEnabled && mIsTouchExplorationEnabled) { - clientState |= AccessibilityManager.STATE_FLAG_TOUCH_EXPLORATION_ENABLED; - } - if (mIsTextHighContrastEnabled) { - clientState |= AccessibilityManager.STATE_FLAG_HIGH_TEXT_CONTRAST_ENABLED; - } - return clientState; - } - - public boolean isHandlingAccessibilityEvents() { - return !mBoundServices.isEmpty() || !mBindingServices.isEmpty(); - } - - public void onSwitchToAnotherUserLocked() { - // Unbind all services. - unbindAllServicesLocked(this); - - // Clear service management state. - mBoundServices.clear(); - mBindingServices.clear(); - - // Clear event management state. - mLastSentClientState = -1; - - // clear UI timeout - mNonInteractiveUiTimeout = 0; - mInteractiveUiTimeout = 0; - - // Clear state persisted in settings. - mEnabledServices.clear(); - mTouchExplorationGrantedServices.clear(); - mIsTouchExplorationEnabled = false; - mIsDisplayMagnificationEnabled = false; - mIsNavBarMagnificationEnabled = false; - mServiceAssignedToAccessibilityButton = null; - mIsNavBarMagnificationAssignedToAccessibilityButton = false; - mIsAutoclickEnabled = false; - mUserNonInteractiveUiTimeout = 0; - mUserInteractiveUiTimeout = 0; - } - - public void addServiceLocked(AccessibilityServiceConnection serviceConnection) { - if (!mBoundServices.contains(serviceConnection)) { - serviceConnection.onAdded(); - mBoundServices.add(serviceConnection); - mComponentNameToServiceMap.put(serviceConnection.mComponentName, serviceConnection); - scheduleNotifyClientsOfServicesStateChangeLocked(this); - } - } - - /** - * Removes a service. - * There are three states to a service here: off, bound, and binding. - * This stops tracking the service as bound. - * - * @param serviceConnection The service. - */ - public void removeServiceLocked(AccessibilityServiceConnection serviceConnection) { - mBoundServices.remove(serviceConnection); - serviceConnection.onRemoved(); - if ((mServiceChangingSoftKeyboardMode != null) - && (mServiceChangingSoftKeyboardMode.equals( - serviceConnection.getServiceInfo().getComponentName()))) { - setSoftKeyboardModeLocked(SHOW_MODE_AUTO, null); - } - // It may be possible to bind a service twice, which confuses the map. Rebuild the map - // to make sure we can still reach a service - mComponentNameToServiceMap.clear(); - for (int i = 0; i < mBoundServices.size(); i++) { - AccessibilityServiceConnection boundClient = mBoundServices.get(i); - mComponentNameToServiceMap.put(boundClient.mComponentName, boundClient); - } - scheduleNotifyClientsOfServicesStateChangeLocked(this); - } - - /** - * Make sure a services disconnected but still 'on' state is reflected in UserState - * There are three states to a service here: off, bound, and binding. - * This drops a service from a bound state, to the binding state. - * The binding state describes the situation where a service is on, but not bound. - * - * @param serviceConnection The service. - */ - public void serviceDisconnectedLocked(AccessibilityServiceConnection serviceConnection) { - removeServiceLocked(serviceConnection); - mBindingServices.add(serviceConnection.getComponentName()); - } - - public Set<ComponentName> getBindingServicesLocked() { - return mBindingServices; - } - - /** - * Returns enabled service list. - */ - public Set<ComponentName> getEnabledServicesLocked() { - return mEnabledServices; - } - - public int getSoftKeyboardShowMode() { - return mSoftKeyboardShowMode; - } - - /** - * Set the soft keyboard mode. This mode is a bit odd, as it spans multiple settings. - * The ACCESSIBILITY_SOFT_KEYBOARD_MODE setting can be checked by the rest of the system - * to see if it should suppress showing the IME. The SHOW_IME_WITH_HARD_KEYBOARD setting - * setting can be changed by the user, and prevents the system from suppressing the soft - * keyboard when the hard keyboard is connected. The hard keyboard setting needs to defer - * to the user's preference, if they have supplied one. - * - * @param newMode The new mode - * @param requester The service requesting the change, so we can undo it when the - * service stops. Set to null if something other than a service is forcing - * the change. - * - * @return Whether or not the soft keyboard mode equals the new mode after the call - */ - public boolean setSoftKeyboardModeLocked(int newMode, @Nullable ComponentName requester) { - if ((newMode != SHOW_MODE_AUTO) && (newMode != SHOW_MODE_HIDDEN) - && (newMode != SHOW_MODE_IGNORE_HARD_KEYBOARD)) - { - Slog.w(LOG_TAG, "Invalid soft keyboard mode"); - return false; - } - if (mSoftKeyboardShowMode == newMode) return true; - - if (newMode == SHOW_MODE_IGNORE_HARD_KEYBOARD) { - if (hasUserOverriddenHardKeyboardSettingLocked()) { - // The user has specified a default for this setting - return false; - } - // Save the original value. But don't do this if the value in settings is already - // the new mode. That happens when we start up after a reboot, and we don't want - // to overwrite the value we had from when we first started controlling the setting. - if (getSoftKeyboardValueFromSettings() != SHOW_MODE_IGNORE_HARD_KEYBOARD) { - setOriginalHardKeyboardValue( - Settings.Secure.getInt(mContext.getContentResolver(), - Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD, 0) != 0); - } - putSecureIntForUser(Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD, 1, mUserId); - } else if (mSoftKeyboardShowMode == SHOW_MODE_IGNORE_HARD_KEYBOARD) { - putSecureIntForUser(Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD, - getOriginalHardKeyboardValue() ? 1 : 0, mUserId); - } - - saveSoftKeyboardValueToSettings(newMode); - mSoftKeyboardShowMode = newMode; - mServiceChangingSoftKeyboardMode = requester; - notifySoftKeyboardShowModeChangedLocked(mSoftKeyboardShowMode); - return true; - } - - /** - * If the settings are inconsistent with the internal state, make the internal state - * match the settings. - */ - public void reconcileSoftKeyboardModeWithSettingsLocked() { - final ContentResolver cr = mContext.getContentResolver(); - final boolean showWithHardKeyboardSettings = - Settings.Secure.getInt(cr, Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD, 0) != 0; - if (mSoftKeyboardShowMode == SHOW_MODE_IGNORE_HARD_KEYBOARD) { - if (!showWithHardKeyboardSettings) { - // The user has overridden the setting. Respect that and prevent further changes - // to this behavior. - setSoftKeyboardModeLocked(SHOW_MODE_AUTO, null); - setUserOverridesHardKeyboardSettingLocked(); - } - } - - // If the setting and the internal state are out of sync, set both to default - if (getSoftKeyboardValueFromSettings() != mSoftKeyboardShowMode) - { - Slog.e(LOG_TAG, - "Show IME setting inconsistent with internal state. Overwriting"); - setSoftKeyboardModeLocked(SHOW_MODE_AUTO, null); - putSecureIntForUser(Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE, - SHOW_MODE_AUTO, mUserId); - } - } - - private void setUserOverridesHardKeyboardSettingLocked() { - final int softKeyboardSetting = Settings.Secure.getInt(mContext.getContentResolver(), - Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE, 0); - putSecureIntForUser(Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE, - softKeyboardSetting | SHOW_MODE_HARD_KEYBOARD_OVERRIDDEN, - mUserId); - } - - private boolean hasUserOverriddenHardKeyboardSettingLocked() { - final int softKeyboardSetting = Settings.Secure.getInt(mContext.getContentResolver(), - Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE, 0); - return (softKeyboardSetting & SHOW_MODE_HARD_KEYBOARD_OVERRIDDEN) - != 0; - } - - private void setOriginalHardKeyboardValue(boolean originalHardKeyboardValue) { - final int oldSoftKeyboardSetting = Settings.Secure.getInt(mContext.getContentResolver(), - Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE, 0); - final int newSoftKeyboardSetting = oldSoftKeyboardSetting - & (~SHOW_MODE_HARD_KEYBOARD_ORIGINAL_VALUE) - | ((originalHardKeyboardValue) ? SHOW_MODE_HARD_KEYBOARD_ORIGINAL_VALUE : 0); - putSecureIntForUser(Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE, - newSoftKeyboardSetting, mUserId); - } - - private void saveSoftKeyboardValueToSettings(int softKeyboardShowMode) { - final int oldSoftKeyboardSetting = Settings.Secure.getInt(mContext.getContentResolver(), - Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE, 0); - final int newSoftKeyboardSetting = oldSoftKeyboardSetting & (~SHOW_MODE_MASK) - | softKeyboardShowMode; - putSecureIntForUser(Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE, - newSoftKeyboardSetting, mUserId); - } - - private int getSoftKeyboardValueFromSettings() { - return Settings.Secure.getInt(mContext.getContentResolver(), - Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE, - SHOW_MODE_AUTO) & SHOW_MODE_MASK; - } - - private boolean getOriginalHardKeyboardValue() { - return (Settings.Secure.getInt(mContext.getContentResolver(), - Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE, 0) - & SHOW_MODE_HARD_KEYBOARD_ORIGINAL_VALUE) != 0; - } - - public boolean getBindInstantServiceAllowed() { - synchronized (mLock) { - return mBindInstantServiceAllowed; - } - } - - public void setBindInstantServiceAllowed(boolean allowed) { - synchronized (mLock) { - mContext.enforceCallingOrSelfPermission( - Manifest.permission.MANAGE_BIND_INSTANT_SERVICE, - "setBindInstantServiceAllowed"); - if (allowed) { - mBindInstantServiceAllowed = allowed; - onUserStateChangedLocked(this); - } - } - } - } - private final class AccessibilityContentObserver extends ContentObserver { private final Uri mTouchExplorationEnabledUri = Settings.Secure.getUriFor( @@ -3057,7 +2670,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub synchronized (mLock) { // Profiles share the accessibility state of the parent. Therefore, // we are checking for changes only the parent settings. - UserState userState = getCurrentUserStateLocked(); + AccessibilityUserState userState = getCurrentUserStateLocked(); if (mTouchExplorationEnabledUri.equals(uri)) { if (readTouchExplorationEnabledSettingLocked(userState)) { diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java index e7f3ccc9f883..d154060d0b73 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityServiceConnection.java @@ -35,7 +35,6 @@ import android.provider.Settings; import android.util.Slog; import android.view.Display; -import com.android.server.accessibility.AccessibilityManagerService.UserState; import com.android.server.wm.WindowManagerInternal; import java.lang.ref.WeakReference; @@ -52,13 +51,13 @@ import java.util.Set; class AccessibilityServiceConnection extends AbstractAccessibilityServiceConnection { private static final String LOG_TAG = "AccessibilityServiceConnection"; /* - Holding a weak reference so there isn't a loop of references. UserState keeps lists of bound - and binding services. These are freed on user changes, but just in case it somehow gets lost - the weak reference will let the memory get GCed. + Holding a weak reference so there isn't a loop of references. AccessibilityUserState keeps + lists of bound and binding services. These are freed on user changes, but just in case it + somehow gets lost the weak reference will let the memory get GCed. Having the reference be null when being called is a very bad sign, but we check the condition. */ - final WeakReference<UserState> mUserStateWeakReference; + final WeakReference<AccessibilityUserState> mUserStateWeakReference; final Intent mIntent; private final Handler mMainHandler; @@ -66,7 +65,7 @@ class AccessibilityServiceConnection extends AbstractAccessibilityServiceConnect private boolean mWasConnectedAndDied; - public AccessibilityServiceConnection(UserState userState, Context context, + AccessibilityServiceConnection(AccessibilityUserState userState, Context context, ComponentName componentName, AccessibilityServiceInfo accessibilityServiceInfo, int id, Handler mainHandler, Object lock, AccessibilitySecurityPolicy securityPolicy, SystemSupport systemSupport, @@ -74,7 +73,7 @@ class AccessibilityServiceConnection extends AbstractAccessibilityServiceConnect SystemActionPerformer systemActionPerfomer, AccessibilityWindowManager awm) { super(context, componentName, accessibilityServiceInfo, id, mainHandler, lock, securityPolicy, systemSupport, windowManagerInternal, systemActionPerfomer, awm); - mUserStateWeakReference = new WeakReference<UserState>(userState); + mUserStateWeakReference = new WeakReference<AccessibilityUserState>(userState); mIntent = new Intent().setComponent(mComponentName); mMainHandler = mainHandler; mIntent.putExtra(Intent.EXTRA_CLIENT_LABEL, @@ -89,13 +88,13 @@ class AccessibilityServiceConnection extends AbstractAccessibilityServiceConnect } public void bindLocked() { - UserState userState = mUserStateWeakReference.get(); + AccessibilityUserState userState = mUserStateWeakReference.get(); if (userState == null) return; final long identity = Binder.clearCallingIdentity(); try { int flags = Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE_WHILE_AWAKE | Context.BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS; - if (userState.getBindInstantServiceAllowed()) { + if (userState.getBindInstantServiceAllowedLocked()) { flags |= Context.BIND_ALLOW_INSTANT; } if (mService == null && mContext.bindServiceAsUser( @@ -109,7 +108,7 @@ class AccessibilityServiceConnection extends AbstractAccessibilityServiceConnect public void unbindLocked() { mContext.unbindService(this); - UserState userState = mUserStateWeakReference.get(); + AccessibilityUserState userState = mUserStateWeakReference.get(); if (userState == null) return; userState.removeServiceLocked(this); mSystemSupport.getMagnificationController().resetAllIfNeeded(mId); @@ -123,7 +122,7 @@ class AccessibilityServiceConnection extends AbstractAccessibilityServiceConnect @Override public void disableSelf() { synchronized (mLock) { - UserState userState = mUserStateWeakReference.get(); + AccessibilityUserState userState = mUserStateWeakReference.get(); if (userState == null) return; if (userState.getEnabledServicesLocked().remove(mComponentName)) { final long identity = Binder.clearCallingIdentity(); @@ -156,7 +155,7 @@ class AccessibilityServiceConnection extends AbstractAccessibilityServiceConnect } } mServiceInterface = IAccessibilityServiceClient.Stub.asInterface(service); - UserState userState = mUserStateWeakReference.get(); + AccessibilityUserState userState = mUserStateWeakReference.get(); if (userState == null) return; userState.addServiceLocked(this); mSystemSupport.onClientChangeLocked(false); @@ -177,7 +176,7 @@ class AccessibilityServiceConnection extends AbstractAccessibilityServiceConnect private void initializeService() { IAccessibilityServiceClient serviceInterface = null; synchronized (mLock) { - UserState userState = mUserStateWeakReference.get(); + AccessibilityUserState userState = mUserStateWeakReference.get(); if (userState == null) return; Set<ComponentName> bindingServices = userState.getBindingServicesLocked(); if (bindingServices.contains(mComponentName) || mWasConnectedAndDied) { @@ -240,7 +239,7 @@ class AccessibilityServiceConnection extends AbstractAccessibilityServiceConnect if (!hasRightsToCurrentUserLocked()) { return false; } - final UserState userState = mUserStateWeakReference.get(); + final AccessibilityUserState userState = mUserStateWeakReference.get(); if (userState == null) return false; return userState.setSoftKeyboardModeLocked(showMode, mComponentName); } @@ -248,8 +247,8 @@ class AccessibilityServiceConnection extends AbstractAccessibilityServiceConnect @Override public int getSoftKeyboardShowMode() { - final UserState userState = mUserStateWeakReference.get(); - return (userState != null) ? userState.getSoftKeyboardShowMode() : 0; + final AccessibilityUserState userState = mUserStateWeakReference.get(); + return (userState != null) ? userState.getSoftKeyboardShowModeLocked() : 0; } @Override @@ -258,7 +257,7 @@ class AccessibilityServiceConnection extends AbstractAccessibilityServiceConnect if (!hasRightsToCurrentUserLocked()) { return false; } - UserState userState = mUserStateWeakReference.get(); + AccessibilityUserState userState = mUserStateWeakReference.get(); return (userState != null) && isAccessibilityButtonAvailableLocked(userState); } } @@ -273,7 +272,7 @@ class AccessibilityServiceConnection extends AbstractAccessibilityServiceConnect return; } mWasConnectedAndDied = true; - UserState userState = mUserStateWeakReference.get(); + AccessibilityUserState userState = mUserStateWeakReference.get(); if (userState != null) { userState.serviceDisconnectedLocked(this); } @@ -283,7 +282,7 @@ class AccessibilityServiceConnection extends AbstractAccessibilityServiceConnect } } - public boolean isAccessibilityButtonAvailableLocked(UserState userState) { + public boolean isAccessibilityButtonAvailableLocked(AccessibilityUserState userState) { // If the service does not request the accessibility button, it isn't available if (!mRequestAccessibilityButton) { return false; @@ -295,8 +294,8 @@ class AccessibilityServiceConnection extends AbstractAccessibilityServiceConnect } // If magnification is on and assigned to the accessibility button, services cannot be - if (userState.mIsNavBarMagnificationEnabled - && userState.mIsNavBarMagnificationAssignedToAccessibilityButton) { + if (userState.isNavBarMagnificationEnabledLocked() + && userState.isNavBarMagnificationAssignedToAccessibilityButtonLocked()) { return false; } @@ -314,13 +313,14 @@ class AccessibilityServiceConnection extends AbstractAccessibilityServiceConnect return true; } else { // With more than one active service, we derive the target from the user's settings - if (userState.mServiceAssignedToAccessibilityButton == null) { + if (userState.getServiceAssignedToAccessibilityButtonLocked() == null) { // If the user has not made an assignment, we treat the button as available to // all services until the user interacts with the button to make an assignment return true; } else { // If an assignment was made, it defines availability - return mComponentName.equals(userState.mServiceAssignedToAccessibilityButton); + return mComponentName.equals( + userState.getServiceAssignedToAccessibilityButtonLocked()); } } } diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java new file mode 100644 index 000000000000..69f1e0e41aad --- /dev/null +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java @@ -0,0 +1,573 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.accessibility; + +import static android.accessibilityservice.AccessibilityService.SHOW_MODE_AUTO; +import static android.accessibilityservice.AccessibilityService.SHOW_MODE_HARD_KEYBOARD_ORIGINAL_VALUE; +import static android.accessibilityservice.AccessibilityService.SHOW_MODE_HARD_KEYBOARD_OVERRIDDEN; +import static android.accessibilityservice.AccessibilityService.SHOW_MODE_HIDDEN; +import static android.accessibilityservice.AccessibilityService.SHOW_MODE_IGNORE_HARD_KEYBOARD; +import static android.accessibilityservice.AccessibilityService.SHOW_MODE_MASK; + +import android.accessibilityservice.AccessibilityService.SoftKeyboardShowMode; +import android.accessibilityservice.AccessibilityServiceInfo; +import android.accessibilityservice.AccessibilityShortcutInfo; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.content.ComponentName; +import android.content.Context; +import android.os.Binder; +import android.os.RemoteCallbackList; +import android.provider.Settings; +import android.util.Slog; +import android.view.accessibility.AccessibilityManager; +import android.view.accessibility.IAccessibilityManagerClient; + +import java.io.FileDescriptor; +import java.io.PrintWriter; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * Class that hold states and settings per user and share between + * {@link AccessibilityManagerService} and {@link AccessibilityServiceConnection}. + */ +class AccessibilityUserState { + private static final String LOG_TAG = AccessibilityUserState.class.getSimpleName(); + + final int mUserId; + + // Non-transient state. + + final RemoteCallbackList<IAccessibilityManagerClient> mUserClients = new RemoteCallbackList<>(); + + // Transient state. + + final ArrayList<AccessibilityServiceConnection> mBoundServices = new ArrayList<>(); + + final Map<ComponentName, AccessibilityServiceConnection> mComponentNameToServiceMap = + new HashMap<>(); + + final List<AccessibilityServiceInfo> mInstalledServices = new ArrayList<>(); + + final List<AccessibilityShortcutInfo> mInstalledShortcuts = new ArrayList<>(); + + final Set<ComponentName> mBindingServices = new HashSet<>(); + + final Set<ComponentName> mEnabledServices = new HashSet<>(); + + final Set<ComponentName> mTouchExplorationGrantedServices = new HashSet<>(); + + private final ServiceInfoChangeListener mServiceInfoChangeListener; + + private ComponentName mServiceAssignedToAccessibilityButton; + + private ComponentName mServiceChangingSoftKeyboardMode; + + private ComponentName mServiceToEnableWithShortcut; + + private boolean mBindInstantServiceAllowed; + private boolean mIsAutoclickEnabled; + private boolean mIsDisplayMagnificationEnabled; + private boolean mIsFilterKeyEventsEnabled; + private boolean mIsNavBarMagnificationAssignedToAccessibilityButton; + private boolean mIsNavBarMagnificationEnabled; + private boolean mIsPerformGesturesEnabled; + private boolean mIsTextHighContrastEnabled; + private boolean mIsTouchExplorationEnabled; + private int mUserInteractiveUiTimeout; + private int mUserNonInteractiveUiTimeout; + private int mNonInteractiveUiTimeout = 0; + private int mInteractiveUiTimeout = 0; + private int mLastSentClientState = -1; + + private Context mContext; + + @SoftKeyboardShowMode + private int mSoftKeyboardShowMode = SHOW_MODE_AUTO; + + interface ServiceInfoChangeListener { + void onServiceInfoChangedLocked(AccessibilityUserState userState); + } + + AccessibilityUserState(int userId, @NonNull Context context, + @NonNull ServiceInfoChangeListener serviceInfoChangeListener) { + mUserId = userId; + mContext = context; + mServiceInfoChangeListener = serviceInfoChangeListener; + } + + boolean isHandlingAccessibilityEventsLocked() { + return !mBoundServices.isEmpty() || !mBindingServices.isEmpty(); + } + + void onSwitchToAnotherUserLocked() { + // Unbind all services. + unbindAllServicesLocked(); + + // Clear service management state. + mBoundServices.clear(); + mBindingServices.clear(); + + // Clear event management state. + mLastSentClientState = -1; + + // clear UI timeout + mNonInteractiveUiTimeout = 0; + mInteractiveUiTimeout = 0; + + // Clear state persisted in settings. + mEnabledServices.clear(); + mTouchExplorationGrantedServices.clear(); + mIsTouchExplorationEnabled = false; + mIsDisplayMagnificationEnabled = false; + mIsNavBarMagnificationEnabled = false; + mServiceAssignedToAccessibilityButton = null; + mIsNavBarMagnificationAssignedToAccessibilityButton = false; + mIsAutoclickEnabled = false; + mUserNonInteractiveUiTimeout = 0; + mUserInteractiveUiTimeout = 0; + } + + void addServiceLocked(AccessibilityServiceConnection serviceConnection) { + if (!mBoundServices.contains(serviceConnection)) { + serviceConnection.onAdded(); + mBoundServices.add(serviceConnection); + mComponentNameToServiceMap.put(serviceConnection.getComponentName(), serviceConnection); + mServiceInfoChangeListener.onServiceInfoChangedLocked(this); + } + } + + /** + * Removes a service. + * There are three states to a service here: off, bound, and binding. + * This stops tracking the service as bound. + * + * @param serviceConnection The service. + */ + void removeServiceLocked(AccessibilityServiceConnection serviceConnection) { + mBoundServices.remove(serviceConnection); + serviceConnection.onRemoved(); + if ((mServiceChangingSoftKeyboardMode != null) + && (mServiceChangingSoftKeyboardMode.equals( + serviceConnection.getServiceInfo().getComponentName()))) { + setSoftKeyboardModeLocked(SHOW_MODE_AUTO, null); + } + // It may be possible to bind a service twice, which confuses the map. Rebuild the map + // to make sure we can still reach a service + mComponentNameToServiceMap.clear(); + for (int i = 0; i < mBoundServices.size(); i++) { + AccessibilityServiceConnection boundClient = mBoundServices.get(i); + mComponentNameToServiceMap.put(boundClient.getComponentName(), boundClient); + } + mServiceInfoChangeListener.onServiceInfoChangedLocked(this); + } + + /** + * Make sure a services disconnected but still 'on' state is reflected in AccessibilityUserState + * There are three states to a service here: off, bound, and binding. + * This drops a service from a bound state, to the binding state. + * The binding state describes the situation where a service is on, but not bound. + * + * @param serviceConnection The service. + */ + void serviceDisconnectedLocked(AccessibilityServiceConnection serviceConnection) { + removeServiceLocked(serviceConnection); + mBindingServices.add(serviceConnection.getComponentName()); + } + + /** + * Set the soft keyboard mode. This mode is a bit odd, as it spans multiple settings. + * The ACCESSIBILITY_SOFT_KEYBOARD_MODE setting can be checked by the rest of the system + * to see if it should suppress showing the IME. The SHOW_IME_WITH_HARD_KEYBOARD setting + * setting can be changed by the user, and prevents the system from suppressing the soft + * keyboard when the hard keyboard is connected. The hard keyboard setting needs to defer + * to the user's preference, if they have supplied one. + * + * @param newMode The new mode + * @param requester The service requesting the change, so we can undo it when the + * service stops. Set to null if something other than a service is forcing + * the change. + * + * @return Whether or not the soft keyboard mode equals the new mode after the call + */ + boolean setSoftKeyboardModeLocked(@SoftKeyboardShowMode int newMode, + @Nullable ComponentName requester) { + if ((newMode != SHOW_MODE_AUTO) + && (newMode != SHOW_MODE_HIDDEN) + && (newMode != SHOW_MODE_IGNORE_HARD_KEYBOARD)) { + Slog.w(LOG_TAG, "Invalid soft keyboard mode"); + return false; + } + if (mSoftKeyboardShowMode == newMode) { + return true; + } + + if (newMode == SHOW_MODE_IGNORE_HARD_KEYBOARD) { + if (hasUserOverriddenHardKeyboardSetting()) { + // The user has specified a default for this setting + return false; + } + // Save the original value. But don't do this if the value in settings is already + // the new mode. That happens when we start up after a reboot, and we don't want + // to overwrite the value we had from when we first started controlling the setting. + if (getSoftKeyboardValueFromSettings() != SHOW_MODE_IGNORE_HARD_KEYBOARD) { + setOriginalHardKeyboardValue(getSecureIntForUser( + Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD, 0, mUserId) != 0); + } + putSecureIntForUser(Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD, 1, mUserId); + } else if (mSoftKeyboardShowMode == SHOW_MODE_IGNORE_HARD_KEYBOARD) { + putSecureIntForUser(Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD, + getOriginalHardKeyboardValue() ? 1 : 0, mUserId); + } + + saveSoftKeyboardValueToSettings(newMode); + mSoftKeyboardShowMode = newMode; + mServiceChangingSoftKeyboardMode = requester; + for (int i = mBoundServices.size() - 1; i >= 0; i--) { + final AccessibilityServiceConnection service = mBoundServices.get(i); + service.notifySoftKeyboardShowModeChangedLocked(mSoftKeyboardShowMode); + } + return true; + } + + @SoftKeyboardShowMode + int getSoftKeyboardShowModeLocked() { + return mSoftKeyboardShowMode; + } + + /** + * If the settings are inconsistent with the internal state, make the internal state + * match the settings. + */ + void reconcileSoftKeyboardModeWithSettingsLocked() { + final boolean showWithHardKeyboardSettings = + getSecureIntForUser(Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD, 0, mUserId) != 0; + if (mSoftKeyboardShowMode == SHOW_MODE_IGNORE_HARD_KEYBOARD) { + if (!showWithHardKeyboardSettings) { + // The user has overridden the setting. Respect that and prevent further changes + // to this behavior. + setSoftKeyboardModeLocked(SHOW_MODE_AUTO, null); + setUserOverridesHardKeyboardSetting(); + } + } + + // If the setting and the internal state are out of sync, set both to default + if (getSoftKeyboardValueFromSettings() != mSoftKeyboardShowMode) { + Slog.e(LOG_TAG, "Show IME setting inconsistent with internal state. Overwriting"); + setSoftKeyboardModeLocked(SHOW_MODE_AUTO, null); + putSecureIntForUser(Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE, + SHOW_MODE_AUTO, mUserId); + } + } + + boolean getBindInstantServiceAllowedLocked() { + return mBindInstantServiceAllowed; + } + + /* Need to have a permission check on callee */ + void setBindInstantServiceAllowedLocked(boolean allowed) { + mBindInstantServiceAllowed = allowed; + } + + Set<ComponentName> getBindingServicesLocked() { + return mBindingServices; + } + + /** + * Returns enabled service list. + */ + Set<ComponentName> getEnabledServicesLocked() { + return mEnabledServices; + } + + List<AccessibilityServiceConnection> getBoundServicesLocked() { + return mBoundServices; + } + + int getClientStateLocked(boolean isUiAutomationRunning) { + int clientState = 0; + final boolean a11yEnabled = isUiAutomationRunning + || isHandlingAccessibilityEventsLocked(); + if (a11yEnabled) { + clientState |= AccessibilityManager.STATE_FLAG_ACCESSIBILITY_ENABLED; + } + // Touch exploration relies on enabled accessibility. + if (a11yEnabled && mIsTouchExplorationEnabled) { + clientState |= AccessibilityManager.STATE_FLAG_TOUCH_EXPLORATION_ENABLED; + } + if (mIsTextHighContrastEnabled) { + clientState |= AccessibilityManager.STATE_FLAG_HIGH_TEXT_CONTRAST_ENABLED; + } + return clientState; + } + + private void setUserOverridesHardKeyboardSetting() { + final int softKeyboardSetting = getSecureIntForUser( + Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE, SHOW_MODE_AUTO, mUserId); + putSecureIntForUser(Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE, + softKeyboardSetting | SHOW_MODE_HARD_KEYBOARD_OVERRIDDEN, + mUserId); + } + + private boolean hasUserOverriddenHardKeyboardSetting() { + final int softKeyboardSetting = getSecureIntForUser( + Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE, SHOW_MODE_AUTO, mUserId); + return (softKeyboardSetting & SHOW_MODE_HARD_KEYBOARD_OVERRIDDEN) + != 0; + } + + private void setOriginalHardKeyboardValue(boolean originalHardKeyboardValue) { + final int oldSoftKeyboardSetting = getSecureIntForUser( + Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE, SHOW_MODE_AUTO, mUserId); + final int newSoftKeyboardSetting = oldSoftKeyboardSetting + & (~SHOW_MODE_HARD_KEYBOARD_ORIGINAL_VALUE) + | ((originalHardKeyboardValue) ? SHOW_MODE_HARD_KEYBOARD_ORIGINAL_VALUE : 0); + putSecureIntForUser(Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE, + newSoftKeyboardSetting, mUserId); + } + + private void saveSoftKeyboardValueToSettings(int softKeyboardShowMode) { + final int oldSoftKeyboardSetting = getSecureIntForUser( + Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE, SHOW_MODE_AUTO, mUserId); + final int newSoftKeyboardSetting = oldSoftKeyboardSetting & (~SHOW_MODE_MASK) + | softKeyboardShowMode; + putSecureIntForUser(Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE, + newSoftKeyboardSetting, mUserId); + } + + private int getSoftKeyboardValueFromSettings() { + return getSecureIntForUser( + Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE, SHOW_MODE_AUTO, mUserId) + & SHOW_MODE_MASK; + } + + private boolean getOriginalHardKeyboardValue() { + return (getSecureIntForUser( + Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE, SHOW_MODE_AUTO, mUserId) + & SHOW_MODE_HARD_KEYBOARD_ORIGINAL_VALUE) != 0; + } + + private void unbindAllServicesLocked() { + final List<AccessibilityServiceConnection> services = mBoundServices; + for (int count = services.size(); count > 0; count--) { + // When the service is unbound, it disappears from the list, so there's no need to + // keep track of the index + services.get(0).unbindLocked(); + } + } + + private int getSecureIntForUser(String key, int def, int userId) { + return Settings.Secure.getIntForUser(mContext.getContentResolver(), key, def, userId); + } + + private void putSecureIntForUser(String key, int value, int userId) { + final long identity = Binder.clearCallingIdentity(); + try { + Settings.Secure.putIntForUser(mContext.getContentResolver(), key, value, userId); + } finally { + Binder.restoreCallingIdentity(identity); + } + } + + void dump(FileDescriptor fd, PrintWriter pw, String[] args) { + pw.append("User state["); + pw.println(); + pw.append(" attributes:{id=").append(String.valueOf(mUserId)); + pw.append(", touchExplorationEnabled=").append(String.valueOf(mIsTouchExplorationEnabled)); + pw.append(", displayMagnificationEnabled=").append(String.valueOf( + mIsDisplayMagnificationEnabled)); + pw.append(", navBarMagnificationEnabled=").append(String.valueOf( + mIsNavBarMagnificationEnabled)); + pw.append(", autoclickEnabled=").append(String.valueOf(mIsAutoclickEnabled)); + pw.append(", nonInteractiveUiTimeout=").append(String.valueOf(mNonInteractiveUiTimeout)); + pw.append(", interactiveUiTimeout=").append(String.valueOf(mInteractiveUiTimeout)); + pw.append(", installedServiceCount=").append(String.valueOf(mInstalledServices.size())); + pw.append("}"); + pw.println(); + pw.append(" Bound services:{"); + final int serviceCount = mBoundServices.size(); + for (int j = 0; j < serviceCount; j++) { + if (j > 0) { + pw.append(", "); + pw.println(); + pw.append(" "); + } + AccessibilityServiceConnection service = mBoundServices.get(j); + service.dump(fd, pw, args); + } + pw.println("}"); + pw.append(" Enabled services:{"); + Iterator<ComponentName> it = mEnabledServices.iterator(); + if (it.hasNext()) { + ComponentName componentName = it.next(); + pw.append(componentName.toShortString()); + while (it.hasNext()) { + componentName = it.next(); + pw.append(", "); + pw.append(componentName.toShortString()); + } + } + pw.println("}"); + pw.append(" Binding services:{"); + it = mBindingServices.iterator(); + if (it.hasNext()) { + ComponentName componentName = it.next(); + pw.append(componentName.toShortString()); + while (it.hasNext()) { + componentName = it.next(); + pw.append(", "); + pw.append(componentName.toShortString()); + } + } + pw.println("}]"); + } + + public boolean isAutoclickEnabledLocked() { + return mIsAutoclickEnabled; + } + + public void setAutoclickEnabledLocked(boolean enabled) { + mIsAutoclickEnabled = enabled; + } + + public boolean isDisplayMagnificationEnabledLocked() { + return mIsDisplayMagnificationEnabled; + } + + public void setDisplayMagnificationEnabledLocked(boolean enabled) { + mIsDisplayMagnificationEnabled = enabled; + } + + public boolean isFilterKeyEventsEnabledLocked() { + return mIsFilterKeyEventsEnabled; + } + + public void setFilterKeyEventsEnabledLocked(boolean enabled) { + mIsFilterKeyEventsEnabled = enabled; + } + + public int getInteractiveUiTimeoutLocked() { + return mInteractiveUiTimeout; + } + + public void setInteractiveUiTimeoutLocked(int timeout) { + mInteractiveUiTimeout = timeout; + } + + public int getLastSentClientStateLocked() { + return mLastSentClientState; + } + + public void setLastSentClientStateLocked(int state) { + mLastSentClientState = state; + } + + public boolean isNavBarMagnificationAssignedToAccessibilityButtonLocked() { + return mIsNavBarMagnificationAssignedToAccessibilityButton; + } + + public void setNavBarMagnificationAssignedToAccessibilityButtonLocked(boolean assigned) { + mIsNavBarMagnificationAssignedToAccessibilityButton = assigned; + } + + public boolean isNavBarMagnificationEnabledLocked() { + return mIsNavBarMagnificationEnabled; + } + + public void setNavBarMagnificationEnabledLocked(boolean enabled) { + mIsNavBarMagnificationEnabled = enabled; + } + + public int getNonInteractiveUiTimeoutLocked() { + return mNonInteractiveUiTimeout; + } + + public void setNonInteractiveUiTimeoutLocked(int timeout) { + mNonInteractiveUiTimeout = timeout; + } + + public boolean isPerformGesturesEnabledLocked() { + return mIsPerformGesturesEnabled; + } + + public void setPerformGesturesEnabledLocked(boolean enabled) { + mIsPerformGesturesEnabled = enabled; + } + + public ComponentName getServiceAssignedToAccessibilityButtonLocked() { + return mServiceAssignedToAccessibilityButton; + } + + public void setServiceAssignedToAccessibilityButtonLocked(ComponentName componentName) { + mServiceAssignedToAccessibilityButton = componentName; + } + + public ComponentName getServiceChangingSoftKeyboardModeLocked() { + return mServiceChangingSoftKeyboardMode; + } + + public void setServiceChangingSoftKeyboardModeLocked( + ComponentName serviceChangingSoftKeyboardMode) { + mServiceChangingSoftKeyboardMode = serviceChangingSoftKeyboardMode; + } + + public ComponentName getServiceToEnableWithShortcutLocked() { + return mServiceToEnableWithShortcut; + } + + public void setServiceToEnableWithShortcutLocked(ComponentName componentName) { + mServiceToEnableWithShortcut = componentName; + } + + public boolean isTextHighContrastEnabledLocked() { + return mIsTextHighContrastEnabled; + } + + public void setTextHighContrastEnabledLocked(boolean enabled) { + mIsTextHighContrastEnabled = enabled; + } + + public boolean isTouchExplorationEnabledLocked() { + return mIsTouchExplorationEnabled; + } + + public void setTouchExplorationEnabledLocked(boolean enabled) { + mIsTouchExplorationEnabled = enabled; + } + + public int getUserInteractiveUiTimeoutLocked() { + return mUserInteractiveUiTimeout; + } + + public void setUserInteractiveUiTimeoutLocked(int timeout) { + mUserInteractiveUiTimeout = timeout; + } + + public int getUserNonInteractiveUiTimeoutLocked() { + return mUserNonInteractiveUiTimeout; + } + + public void setUserNonInteractiveUiTimeoutLocked(int timeout) { + mUserNonInteractiveUiTimeout = timeout; + } +} diff --git a/services/autofill/java/com/android/server/autofill/ui/SaveUi.java b/services/autofill/java/com/android/server/autofill/ui/SaveUi.java index b35300ceb399..7d129eaa5390 100644 --- a/services/autofill/java/com/android/server/autofill/ui/SaveUi.java +++ b/services/autofill/java/com/android/server/autofill/ui/SaveUi.java @@ -286,7 +286,7 @@ final class SaveUi { window.addFlags(WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH); - window.addPrivateFlags(WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS); + window.addPrivateFlags(WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS); window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN); window.setGravity(Gravity.BOTTOM | Gravity.CENTER); window.setCloseOnTouchOutside(true); diff --git a/services/backup/backuplib/java/com/android/server/backup/TransportManager.java b/services/backup/backuplib/java/com/android/server/backup/TransportManager.java index de48f4b13d7b..30ce4cf2fd3f 100644 --- a/services/backup/backuplib/java/com/android/server/backup/TransportManager.java +++ b/services/backup/backuplib/java/com/android/server/backup/TransportManager.java @@ -93,8 +93,7 @@ public class TransportManager { mTransportWhitelist = Preconditions.checkNotNull(whitelist); mCurrentTransportName = selectedTransport; mTransportStats = new TransportStats(); - mTransportClientManager = TransportClientManager.createEncryptingClientManager(mUserId, - context, mTransportStats); + mTransportClientManager = new TransportClientManager(mUserId, context, mTransportStats); } @VisibleForTesting diff --git a/services/core/java/android/content/pm/PackageManagerInternal.java b/services/core/java/android/content/pm/PackageManagerInternal.java index d6515f38eeaa..dc24cffa54a4 100644 --- a/services/core/java/android/content/pm/PackageManagerInternal.java +++ b/services/core/java/android/content/pm/PackageManagerInternal.java @@ -60,6 +60,7 @@ public abstract class PackageManagerInternal { public static final int PACKAGE_INCIDENT_REPORT_APPROVER = 10; public static final int PACKAGE_APP_PREDICTOR = 11; public static final int PACKAGE_TELEPHONY = 12; + public static final int PACKAGE_WIFI = 13; @IntDef(value = { PACKAGE_SYSTEM, PACKAGE_SETUP_WIZARD, @@ -74,6 +75,7 @@ public abstract class PackageManagerInternal { PACKAGE_INCIDENT_REPORT_APPROVER, PACKAGE_APP_PREDICTOR, PACKAGE_TELEPHONY, + PACKAGE_WIFI, }) @Retention(RetentionPolicy.SOURCE) public @interface KnownPackage {} diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java index e0f60b43f8c8..0bb72cb9d554 100644 --- a/services/core/java/com/android/server/ConnectivityService.java +++ b/services/core/java/com/android/server/ConnectivityService.java @@ -1149,7 +1149,6 @@ public class ConnectivityService extends IConnectivityManager.Stub private static NetworkCapabilities createDefaultNetworkCapabilitiesForUid(int uid) { final NetworkCapabilities netCap = new NetworkCapabilities(); netCap.addCapability(NET_CAPABILITY_INTERNET); - netCap.addCapability(NET_CAPABILITY_NOT_RESTRICTED); netCap.removeCapability(NET_CAPABILITY_NOT_VPN); netCap.setSingleUid(uid); return netCap; @@ -1159,7 +1158,6 @@ public class ConnectivityService extends IConnectivityManager.Stub int transportType, NetworkRequest.Type type) { final NetworkCapabilities netCap = new NetworkCapabilities(); netCap.addCapability(NET_CAPABILITY_INTERNET); - netCap.addCapability(NET_CAPABILITY_NOT_RESTRICTED); if (transportType > -1) { netCap.addTransportType(transportType); } diff --git a/services/core/java/com/android/server/GnssManagerService.java b/services/core/java/com/android/server/GnssManagerService.java index b6e619effb08..44a823475b7e 100644 --- a/services/core/java/com/android/server/GnssManagerService.java +++ b/services/core/java/com/android/server/GnssManagerService.java @@ -17,6 +17,7 @@ package com.android.server; import android.Manifest; +import android.annotation.NonNull; import android.app.ActivityManager; import android.app.AppOpsManager; import android.content.Context; @@ -298,7 +299,8 @@ public class GnssManagerService { * @param packageName name of requesting package * @return true if callback is successfully added, false otherwise */ - public boolean addGnssBatchingCallback(IBatchedLocationCallback callback, String packageName) { + public boolean addGnssBatchingCallback(IBatchedLocationCallback callback, String packageName, + @NonNull String listenerIdentity) { mContext.enforceCallingPermission( android.Manifest.permission.LOCATION_HARDWARE, "Location Hardware permission not granted to access hardware batching"); @@ -316,7 +318,8 @@ public class GnssManagerService { } CallerIdentity callerIdentity = - new CallerIdentity(Binder.getCallingUid(), Binder.getCallingPid(), packageName); + new CallerIdentity(Binder.getCallingUid(), Binder.getCallingPid(), packageName, + listenerIdentity); synchronized (mGnssBatchingLock) { mGnssBatchingCallback = callback; mGnssBatchingDeathCallback = @@ -494,7 +497,7 @@ public class GnssManagerService { private <TListener extends IInterface> boolean addGnssDataListenerLocked( TListener listener, String packageName, - String listenerName, + @NonNull String listenerIdentifier, RemoteListenerHelper<TListener> gnssDataProvider, ArrayMap<IBinder, LinkedListener<TListener>> gnssDataListeners, @@ -513,10 +516,11 @@ public class GnssManagerService { } CallerIdentity callerIdentity = - new CallerIdentity(Binder.getCallingUid(), Binder.getCallingPid(), packageName); + new CallerIdentity(Binder.getCallingUid(), Binder.getCallingPid(), packageName, + listenerIdentifier); LinkedListener<TListener> linkedListener = new LocationManagerServiceUtils.LinkedListener<>( - listener, listenerName, callerIdentity, binderDeathCallback); + listener, listenerIdentifier, callerIdentity, binderDeathCallback); IBinder binder = listener.asBinder(); if (!linkedListener.linkToListenerDeathNotificationLocked(binder)) { return false; @@ -606,7 +610,7 @@ public class GnssManagerService { return addGnssDataListenerLocked( listener, packageName, - "GnssStatusListener", + "Gnss status", mGnssStatusProvider, mGnssStatusListeners, this::unregisterGnssStatusCallback); @@ -632,12 +636,13 @@ public class GnssManagerService { * @return true if listener is successfully added, false otherwise */ public boolean addGnssMeasurementsListener( - IGnssMeasurementsListener listener, String packageName) { + IGnssMeasurementsListener listener, String packageName, + @NonNull String listenerIdentifier) { synchronized (mGnssMeasurementsListeners) { return addGnssDataListenerLocked( listener, packageName, - "GnssMeasurementsListener", + listenerIdentifier, mGnssMeasurementsProvider, mGnssMeasurementsListeners, this::removeGnssMeasurementsListener); @@ -689,12 +694,13 @@ public class GnssManagerService { * @return true if listener is successfully added, false otherwise */ public boolean addGnssNavigationMessageListener( - IGnssNavigationMessageListener listener, String packageName) { + IGnssNavigationMessageListener listener, String packageName, + @NonNull String listenerIdentifier) { synchronized (mGnssNavigationMessageListeners) { return addGnssDataListenerLocked( listener, packageName, - "GnssNavigationMessageListener", + listenerIdentifier, mGnssNavigationMessageProvider, mGnssNavigationMessageListeners, this::removeGnssNavigationMessageListener); diff --git a/services/core/java/com/android/server/LocationManagerService.java b/services/core/java/com/android/server/LocationManagerService.java index a6f4f6aa81eb..aa22feb56534 100644 --- a/services/core/java/com/android/server/LocationManagerService.java +++ b/services/core/java/com/android/server/LocationManagerService.java @@ -1223,8 +1223,10 @@ public class LocationManagerService extends ILocationManager.Stub { PowerManager.WakeLock mWakeLock; private Receiver(ILocationListener listener, PendingIntent intent, int pid, int uid, - String packageName, WorkSource workSource, boolean hideFromAppOps) { - super(new CallerIdentity(uid, pid, packageName), "LocationListener"); + String packageName, WorkSource workSource, boolean hideFromAppOps, + @NonNull String listenerIdentifier) { + super(new CallerIdentity(uid, pid, packageName, listenerIdentifier), + "LocationListener"); mListener = listener; mPendingIntent = intent; if (listener != null) { @@ -1532,9 +1534,12 @@ public class LocationManagerService extends ILocationManager.Stub { } @Override - public boolean addGnssBatchingCallback(IBatchedLocationCallback callback, String packageName) { + public boolean addGnssBatchingCallback(IBatchedLocationCallback callback, String packageName, + String listenerIdentifier) { + Preconditions.checkNotNull(listenerIdentifier); + return mGnssManagerService == null ? false : mGnssManagerService.addGnssBatchingCallback( - callback, packageName); + callback, packageName, listenerIdentifier); } @Override @@ -1696,11 +1701,12 @@ public class LocationManagerService extends ILocationManager.Stub { } } - private boolean reportLocationAccessNoThrow( - int pid, int uid, String packageName, int allowedResolutionLevel) { + private boolean reportLocationAccessNoThrow(int pid, int uid, String packageName, + int allowedResolutionLevel, @Nullable String message) { int op = resolutionLevelToOp(allowedResolutionLevel); if (op >= 0) { - if (mAppOps.noteOpNoThrow(op, uid, packageName) != AppOpsManager.MODE_ALLOWED) { + if (mAppOps.noteOpNoThrow(op, uid, packageName, message) + != AppOpsManager.MODE_ALLOWED) { return false; } } @@ -2133,12 +2139,13 @@ public class LocationManagerService extends ILocationManager.Stub { @GuardedBy("mLock") private Receiver getReceiverLocked(ILocationListener listener, int pid, int uid, - String packageName, WorkSource workSource, boolean hideFromAppOps) { + String packageName, WorkSource workSource, boolean hideFromAppOps, + @NonNull String listenerIdentifier) { IBinder binder = listener.asBinder(); Receiver receiver = mReceivers.get(binder); if (receiver == null) { receiver = new Receiver(listener, null, pid, uid, packageName, workSource, - hideFromAppOps); + hideFromAppOps, listenerIdentifier); if (!receiver.linkToListenerDeathNotificationLocked( receiver.getListener().asBinder())) { return null; @@ -2150,11 +2157,11 @@ public class LocationManagerService extends ILocationManager.Stub { @GuardedBy("mLock") private Receiver getReceiverLocked(PendingIntent intent, int pid, int uid, String packageName, - WorkSource workSource, boolean hideFromAppOps) { + WorkSource workSource, boolean hideFromAppOps, @NonNull String listenerIdentifier) { Receiver receiver = mReceivers.get(intent); if (receiver == null) { receiver = new Receiver(null, intent, pid, uid, packageName, workSource, - hideFromAppOps); + hideFromAppOps, listenerIdentifier); mReceivers.put(intent, receiver); } return receiver; @@ -2216,7 +2223,9 @@ public class LocationManagerService extends ILocationManager.Stub { @Override public void requestLocationUpdates(LocationRequest request, ILocationListener listener, - PendingIntent intent, String packageName) { + PendingIntent intent, String packageName, String listenerIdentifier) { + Preconditions.checkNotNull(listenerIdentifier); + synchronized (mLock) { if (request == null) request = DEFAULT_LOCATION_REQUEST; checkPackageName(packageName); @@ -2271,10 +2280,10 @@ public class LocationManagerService extends ILocationManager.Stub { Receiver receiver; if (intent != null) { receiver = getReceiverLocked(intent, pid, uid, packageName, workSource, - hideFromAppOps); + hideFromAppOps, listenerIdentifier); } else { receiver = getReceiverLocked(listener, pid, uid, packageName, workSource, - hideFromAppOps); + hideFromAppOps, listenerIdentifier); } requestLocationUpdatesLocked(sanitizedRequest, receiver, uid, packageName); } finally { @@ -2343,9 +2352,9 @@ public class LocationManagerService extends ILocationManager.Stub { synchronized (mLock) { Receiver receiver; if (intent != null) { - receiver = getReceiverLocked(intent, pid, uid, packageName, null, false); + receiver = getReceiverLocked(intent, pid, uid, packageName, null, false, ""); } else { - receiver = getReceiverLocked(listener, pid, uid, packageName, null, false); + receiver = getReceiverLocked(listener, pid, uid, packageName, null, false, ""); } long identity = Binder.clearCallingIdentity(); @@ -2464,8 +2473,8 @@ public class LocationManagerService extends ILocationManager.Stub { } // Don't report location access if there is no last location to deliver. if (lastLocation != null) { - if (!reportLocationAccessNoThrow( - pid, uid, packageName, allowedResolutionLevel)) { + if (!reportLocationAccessNoThrow(pid, uid, packageName, allowedResolutionLevel, + null)) { if (D) { Log.d(TAG, "not returning last loc for no op app: " + packageName); } @@ -2519,7 +2528,9 @@ public class LocationManagerService extends ILocationManager.Stub { @Override public void requestGeofence(LocationRequest request, Geofence geofence, PendingIntent intent, - String packageName) { + String packageName, String listenerIdentifier) { + Preconditions.checkNotNull(listenerIdentifier); + if (request == null) request = DEFAULT_LOCATION_REQUEST; int allowedResolutionLevel = getCallerAllowedResolutionLevel(); checkResolutionLevelIsSufficientForGeofenceUse(allowedResolutionLevel); @@ -2564,9 +2575,8 @@ public class LocationManagerService extends ILocationManager.Stub { mActivityManager.getPackageImportance(packageName)); } - mGeofenceManager.addFence(sanitizedRequest, geofence, intent, - allowedResolutionLevel, - uid, packageName); + mGeofenceManager.addFence(sanitizedRequest, geofence, intent, allowedResolutionLevel, + uid, packageName, listenerIdentifier); } finally { Binder.restoreCallingIdentity(identity); } @@ -2613,10 +2623,13 @@ public class LocationManagerService extends ILocationManager.Stub { } @Override - public boolean addGnssMeasurementsListener( - IGnssMeasurementsListener listener, String packageName) { + public boolean addGnssMeasurementsListener(IGnssMeasurementsListener listener, + String packageName, String listenerIdentifier) { + Preconditions.checkNotNull(listenerIdentifier); + return mGnssManagerService == null ? false - : mGnssManagerService.addGnssMeasurementsListener(listener, packageName); + : mGnssManagerService.addGnssMeasurementsListener(listener, packageName, + listenerIdentifier); } @Override @@ -2643,10 +2656,13 @@ public class LocationManagerService extends ILocationManager.Stub { } @Override - public boolean addGnssNavigationMessageListener( - IGnssNavigationMessageListener listener, String packageName) { + public boolean addGnssNavigationMessageListener(IGnssNavigationMessageListener listener, + String packageName, String listenerIdentifier) { + Preconditions.checkNotNull(listenerIdentifier); + return mGnssManagerService == null ? false - : mGnssManagerService.addGnssNavigationMessageListener(listener, packageName); + : mGnssManagerService.addGnssNavigationMessageListener(listener, packageName, + listenerIdentifier); } @Override @@ -2952,7 +2968,8 @@ public class LocationManagerService extends ILocationManager.Stub { receiver.mCallerIdentity.mPid, receiver.mCallerIdentity.mUid, receiver.mCallerIdentity.mPackageName, - receiver.mAllowedResolutionLevel)) { + receiver.mAllowedResolutionLevel, + "Location sent to " + receiver.mCallerIdentity.mListenerIdentifier)) { if (D) { Log.d(TAG, "skipping loc update for no op app: " + receiver.mCallerIdentity.mPackageName); diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 8c672a118843..55cd9339e68c 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -900,7 +900,7 @@ public class ActivityManagerService extends IActivityManager.Stub // The other observer methods are unused @Override - public void onIntentStarted(Intent intent) { + public void onIntentStarted(Intent intent, long timestampNs) { } @Override @@ -912,7 +912,11 @@ public class ActivityManagerService extends IActivityManager.Stub } @Override - public void onActivityLaunchFinished(byte[] finalActivity) { + public void onActivityLaunchFinished(byte[] finalActivity, long timestampNs) { + } + + @Override + public void onReportFullyDrawn(byte[] finalActivity, long timestampNs) { } }; @@ -8658,7 +8662,7 @@ public class ActivityManagerService extends IActivityManager.Stub lp.format = v.getBackground().getOpacity(); lp.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE; - lp.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS; + lp.privateFlags |= WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS; ((WindowManager)mContext.getSystemService( Context.WINDOW_SERVICE)).addView(v, lp); } diff --git a/services/core/java/com/android/server/am/AppErrorDialog.java b/services/core/java/com/android/server/am/AppErrorDialog.java index a80a5b5211ea..852c9b65e3c0 100644 --- a/services/core/java/com/android/server/am/AppErrorDialog.java +++ b/services/core/java/com/android/server/am/AppErrorDialog.java @@ -92,7 +92,7 @@ final class AppErrorDialog extends BaseErrorDialog implements View.OnClickListen WindowManager.LayoutParams attrs = getWindow().getAttributes(); attrs.setTitle("Application Error: " + mProc.info.processName); attrs.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_SYSTEM_ERROR - | WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS; + | WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS; getWindow().setAttributes(attrs); if (mProc.isPersistent()) { getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ERROR); diff --git a/services/core/java/com/android/server/am/AppNotRespondingDialog.java b/services/core/java/com/android/server/am/AppNotRespondingDialog.java index cb76e2fafebd..65d7e01a3e0c 100644 --- a/services/core/java/com/android/server/am/AppNotRespondingDialog.java +++ b/services/core/java/com/android/server/am/AppNotRespondingDialog.java @@ -94,7 +94,7 @@ final class AppNotRespondingDialog extends BaseErrorDialog implements View.OnCli WindowManager.LayoutParams attrs = getWindow().getAttributes(); attrs.setTitle("Application Not Responding: " + mProc.info.processName); attrs.privateFlags = WindowManager.LayoutParams.PRIVATE_FLAG_SYSTEM_ERROR | - WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS; + WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS; getWindow().setAttributes(attrs); } diff --git a/services/core/java/com/android/server/am/UserSwitchingDialog.java b/services/core/java/com/android/server/am/UserSwitchingDialog.java index 98f5557903d6..bbf57728a20a 100644 --- a/services/core/java/com/android/server/am/UserSwitchingDialog.java +++ b/services/core/java/com/android/server/am/UserSwitchingDialog.java @@ -78,7 +78,7 @@ class UserSwitchingDialog extends AlertDialog WindowManager.LayoutParams attrs = getWindow().getAttributes(); attrs.privateFlags = WindowManager.LayoutParams.PRIVATE_FLAG_SYSTEM_ERROR | - WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS; + WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS; getWindow().setAttributes(attrs); } diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java index 6c4cc2d43866..2ac6eb01f9da 100644 --- a/services/core/java/com/android/server/appop/AppOpsService.java +++ b/services/core/java/com/android/server/appop/AppOpsService.java @@ -68,6 +68,7 @@ import android.hardware.camera2.CameraDevice.CAMERA_AUDIO_RESTRICTION; import android.net.Uri; import android.os.AsyncTask; import android.os.Binder; +import android.os.Build; import android.os.Bundle; import android.os.Handler; import android.os.IBinder; @@ -1398,6 +1399,12 @@ public class AppOpsService extends IAppOpsService.Stub { } private void updatePermissionRevokedCompat(int uid, int switchCode, int mode) { + PackageManagerInternal packageManagerInternal = LocalServices.getService( + PackageManagerInternal.class); + if (packageManagerInternal.getUidTargetSdkVersion(uid) >= Build.VERSION_CODES.M) { + return; + } + PackageManager packageManager = mContext.getPackageManager(); String[] packageNames = packageManager.getPackagesForUid(uid); if (ArrayUtils.isEmpty(packageNames)) { diff --git a/services/core/java/com/android/server/hdmi/Constants.java b/services/core/java/com/android/server/hdmi/Constants.java index 96ba8ef6b20e..648e07a01c1c 100644 --- a/services/core/java/com/android/server/hdmi/Constants.java +++ b/services/core/java/com/android/server/hdmi/Constants.java @@ -394,14 +394,10 @@ final class Constants { static final String PROPERTY_PREFERRED_ADDRESS_PLAYBACK = "persist.sys.hdmi.addr.playback"; static final String PROPERTY_PREFERRED_ADDRESS_TV = "persist.sys.hdmi.addr.tv"; - // TODO(OEM): Set this to false to keep the playback device in sleep upon hotplug event. // True by default. static final String PROPERTY_WAKE_ON_HOTPLUG = "ro.hdmi.wake_on_hotplug"; - // TODO(OEM): Set this to true to enable 'Set Menu Language' feature. False by default. - static final String PROPERTY_SET_MENU_LANGUAGE = "ro.hdmi.set_menu_language"; - /** * Property to save the ARC port id on system audio device. * <p>When ARC is initiated, this port will be used to turn on ARC. diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java index 61d4d4bf9ac1..211d0286e49f 100644 --- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java +++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceAudioSystem.java @@ -1238,8 +1238,8 @@ public class HdmiCecLocalDeviceAudioSystem extends HdmiCecLocalDeviceSource { } // Wake up if the current device if ready to route. mService.wakeUp(); - if (getLocalActivePort() == portId) { - HdmiLogger.debug("Not switching to the same port " + portId); + if ((getLocalActivePort() == portId) && (portId != Constants.CEC_SWITCH_ARC)) { + HdmiLogger.debug("Not switching to the same port " + portId + " except for arc"); return; } // Switch to HOME if the current active port is not HOME yet diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java index 413e7a087434..09443242d499 100644 --- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java +++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java @@ -24,6 +24,7 @@ import android.os.PowerManager; import android.os.PowerManager.WakeLock; import android.os.SystemProperties; import android.provider.Settings.Global; +import android.sysprop.HdmiProperties; import android.util.Slog; import com.android.internal.annotations.VisibleForTesting; @@ -47,7 +48,7 @@ public class HdmiCecLocalDevicePlayback extends HdmiCecLocalDeviceSource { SystemProperties.getBoolean(Constants.PROPERTY_WAKE_ON_HOTPLUG, true); private static final boolean SET_MENU_LANGUAGE = - SystemProperties.getBoolean(Constants.PROPERTY_SET_MENU_LANGUAGE, false); + HdmiProperties.set_menu_language().orElse(false); // Used to keep the device awake while it is the active source. For devices that // cannot wake up via CEC commands, this address the inconvenience of having to diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java index f1af73a12276..362955d589af 100644 --- a/services/core/java/com/android/server/input/InputManagerService.java +++ b/services/core/java/com/android/server/input/InputManagerService.java @@ -199,8 +199,7 @@ public class InputManagerService extends IInputManager.Stub int deviceId, int sourceMask, int sw); private static native boolean nativeHasKeys(long ptr, int deviceId, int sourceMask, int[] keyCodes, boolean[] keyExists); - private static native void nativeRegisterInputChannel(long ptr, InputChannel inputChannel, - int displayId); + private static native void nativeRegisterInputChannel(long ptr, InputChannel inputChannel); private static native void nativeRegisterInputMonitor(long ptr, InputChannel inputChannel, int displayId, boolean isGestureMonitor); private static native void nativeUnregisterInputChannel(long ptr, InputChannel inputChannel); @@ -525,7 +524,6 @@ public class InputManagerService extends IInputManager.Stub throw new IllegalArgumentException("displayId must >= 0."); } - final long ident = Binder.clearCallingIdentity(); try { InputChannel[] inputChannels = InputChannel.openInputChannelPair(inputChannelName); @@ -533,7 +531,7 @@ public class InputManagerService extends IInputManager.Stub inputChannels[0].setToken(host.asBinder()); nativeRegisterInputMonitor(mPtr, inputChannels[0], displayId, true /*isGestureMonitor*/); - return new InputMonitor(inputChannelName, inputChannels[1], host); + return new InputMonitor(inputChannels[1], host); } finally { Binder.restoreCallingIdentity(ident); } @@ -551,7 +549,7 @@ public class InputManagerService extends IInputManager.Stub } inputChannel.setToken(new Binder()); - nativeRegisterInputChannel(mPtr, inputChannel, Display.INVALID_DISPLAY); + nativeRegisterInputChannel(mPtr, inputChannel); } /** diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java index b7fcd3f19334..471fa72b1957 100644 --- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java +++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java @@ -3570,10 +3570,14 @@ public class InputMethodManagerService extends IInputMethodManager.Stub return; } if (!setVisible) { - // Client hides the IME directly. - if (mCurClient != null && mCurClient.client != null) { - executeOrSendMessage(mCurClient.client, mCaller.obtainMessageIO( - MSG_APPLY_IME_VISIBILITY, setVisible ? 1 : 0, mCurClient)); + if (mCurClient != null) { + // IMMS only knows of focused window, not the actual IME target. + // e.g. it isn't aware of any window that has both + // NOT_FOCUSABLE, ALT_FOCUSABLE_IM flags set and can the IME target. + // Send it to window manager to hide IME from IME target window. + // TODO(b/139861270): send to mCurClient.client once IMMS is aware of + // actual IME target. + mWindowManagerInternal.hideIme(mCurClient.selfReportedDisplayId); } } else { // Send to window manager to show IME after IME layout finishes. @@ -4208,7 +4212,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub // with other IME windows based on type vs. grouping based on whichever token happens // to get selected by the system later on. attrs.token = mSwitchingDialogToken; - attrs.privateFlags |= LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS; + attrs.privateFlags |= LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS; attrs.setTitle("Select input method"); w.setAttributes(attrs); updateSystemUiLocked(mImeWindowVis, mBackDisposition); diff --git a/services/core/java/com/android/server/location/CallerIdentity.java b/services/core/java/com/android/server/location/CallerIdentity.java index da31d0b8e9a3..61e5d1f31f73 100644 --- a/services/core/java/com/android/server/location/CallerIdentity.java +++ b/services/core/java/com/android/server/location/CallerIdentity.java @@ -16,6 +16,8 @@ package com.android.server.location; +import android.annotation.NonNull; + /** * Represents the calling process's uid, pid, and package name. */ @@ -23,10 +25,13 @@ public class CallerIdentity { public final int mUid; public final int mPid; public final String mPackageName; + public final @NonNull String mListenerIdentifier; - public CallerIdentity(int uid, int pid, String packageName) { + public CallerIdentity(int uid, int pid, String packageName, + @NonNull String listenerIdentifier) { mUid = uid; mPid = pid; mPackageName = packageName; + mListenerIdentifier = listenerIdentifier; } } diff --git a/services/core/java/com/android/server/location/GeofenceManager.java b/services/core/java/com/android/server/location/GeofenceManager.java index a1922067e7cf..895c4b388eeb 100644 --- a/services/core/java/com/android/server/location/GeofenceManager.java +++ b/services/core/java/com/android/server/location/GeofenceManager.java @@ -16,6 +16,7 @@ package com.android.server.location; +import android.annotation.NonNull; import android.app.AppOpsManager; import android.app.PendingIntent; import android.content.ContentResolver; @@ -151,14 +152,16 @@ public class GeofenceManager implements LocationListener, PendingIntent.OnFinish } public void addFence(LocationRequest request, Geofence geofence, PendingIntent intent, - int allowedResolutionLevel, int uid, String packageName) { + int allowedResolutionLevel, int uid, String packageName, + @NonNull String listenerIdentifier) { if (D) { Slog.d(TAG, "addFence: request=" + request + ", geofence=" + geofence + ", intent=" + intent + ", uid=" + uid + ", packageName=" + packageName); } GeofenceState state = new GeofenceState(geofence, - request.getExpireAt(), allowedResolutionLevel, uid, packageName, intent); + request.getExpireAt(), allowedResolutionLevel, uid, packageName, listenerIdentifier, + intent); synchronized (mLock) { // first make sure it doesn't already exist for (int i = mFences.size() - 1; i >= 0; i--) { @@ -301,7 +304,8 @@ public class GeofenceManager implements LocationListener, PendingIntent.OnFinish int op = LocationManagerService.resolutionLevelToOp(state.mAllowedResolutionLevel); if (op >= 0) { if (mAppOps.noteOpNoThrow(AppOpsManager.OP_FINE_LOCATION, state.mUid, - state.mPackageName) != AppOpsManager.MODE_ALLOWED) { + state.mPackageName, state.mListenerIdentifier) + != AppOpsManager.MODE_ALLOWED) { if (D) { Slog.d(TAG, "skipping geofence processing for no op app: " + state.mPackageName); diff --git a/services/core/java/com/android/server/location/GeofenceState.java b/services/core/java/com/android/server/location/GeofenceState.java index 3ebe20a7e8e6..fe0719df1ced 100644 --- a/services/core/java/com/android/server/location/GeofenceState.java +++ b/services/core/java/com/android/server/location/GeofenceState.java @@ -17,6 +17,7 @@ package com.android.server.location; +import android.annotation.NonNull; import android.app.PendingIntent; import android.location.Geofence; import android.location.Location; @@ -38,13 +39,14 @@ public class GeofenceState { public final int mAllowedResolutionLevel; public final int mUid; public final String mPackageName; + public final @NonNull String mListenerIdentifier; public final PendingIntent mIntent; int mState; // current state double mDistanceToCenter; // current distance to center of fence - public GeofenceState(Geofence fence, long expireAt, - int allowedResolutionLevel, int uid, String packageName, PendingIntent intent) { + public GeofenceState(Geofence fence, long expireAt, int allowedResolutionLevel, int uid, + String packageName, @NonNull String listenerIdentifier, PendingIntent intent) { mState = STATE_UNKNOWN; mDistanceToCenter = Double.MAX_VALUE; @@ -53,6 +55,7 @@ public class GeofenceState { mAllowedResolutionLevel = allowedResolutionLevel; mUid = uid; mPackageName = packageName; + mListenerIdentifier = listenerIdentifier; mIntent = intent; mLocation = new Location(""); diff --git a/services/core/java/com/android/server/location/RemoteListenerHelper.java b/services/core/java/com/android/server/location/RemoteListenerHelper.java index aa8a25a36333..0929d93513b9 100644 --- a/services/core/java/com/android/server/location/RemoteListenerHelper.java +++ b/services/core/java/com/android/server/location/RemoteListenerHelper.java @@ -182,7 +182,9 @@ public abstract class RemoteListenerHelper<TListener extends IInterface> { } return mAppOps.noteOpNoThrow(AppOpsManager.OP_FINE_LOCATION, callerIdentity.mUid, - callerIdentity.mPackageName) == AppOpsManager.MODE_ALLOWED; + callerIdentity.mPackageName, + "Location sent to " + callerIdentity.mListenerIdentifier) + == AppOpsManager.MODE_ALLOWED; } protected void logPermissionDisabledEventNotReported(String tag, String packageName, diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java index bad484fa3807..63ba138fdfca 100644 --- a/services/core/java/com/android/server/locksettings/LockSettingsService.java +++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java @@ -118,6 +118,7 @@ import com.android.internal.widget.ILockSettings; import com.android.internal.widget.LockPatternUtils; import com.android.internal.widget.LockPatternUtils.CredentialType; import com.android.internal.widget.LockSettingsInternal; +import com.android.internal.widget.LockscreenCredential; import com.android.internal.widget.VerifyCredentialResponse; import com.android.server.LocalServices; import com.android.server.ServiceThread; @@ -2058,7 +2059,8 @@ public class LockSettingsService extends ILockSettings.Stub { @UserIdInt int userHandle) { synchronized (this) { mUserPasswordMetrics.put(userHandle, - PasswordMetrics.computeForCredential(credentialType, password)); + PasswordMetrics.computeForCredential( + LockscreenCredential.createRaw(credentialType, password))); } } diff --git a/services/core/java/com/android/server/locksettings/LockSettingsShellCommand.java b/services/core/java/com/android/server/locksettings/LockSettingsShellCommand.java index a5d59e3c8884..0a8e5bd7ad4b 100644 --- a/services/core/java/com/android/server/locksettings/LockSettingsShellCommand.java +++ b/services/core/java/com/android/server/locksettings/LockSettingsShellCommand.java @@ -16,16 +16,12 @@ package com.android.server.locksettings; -import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC; -import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_NUMERIC; - -import static com.android.internal.widget.LockPatternUtils.stringToPattern; - import android.app.ActivityManager; import android.os.ShellCommand; import com.android.internal.widget.LockPatternUtils; import com.android.internal.widget.LockPatternUtils.RequestThrottledException; +import com.android.internal.widget.LockscreenCredential; import java.io.PrintWriter; @@ -189,31 +185,49 @@ class LockSettingsShellCommand extends ShellCommand { mLockPatternUtils.isSyntheticPasswordEnabled())); } + private LockscreenCredential getOldCredential() { + if (mLockPatternUtils.isLockPasswordEnabled(mCurrentUserId)) { + final int quality = mLockPatternUtils.getKeyguardStoredPasswordQuality(mCurrentUserId); + if (LockPatternUtils.isQualityAlphabeticPassword(quality)) { + return LockscreenCredential.createPassword(mOld); + } else { + return LockscreenCredential.createPin(mOld); + } + } else if (mLockPatternUtils.isLockPatternEnabled(mCurrentUserId)) { + return LockscreenCredential.createPattern(LockPatternUtils.byteArrayToPattern( + mOld.getBytes())); + } else { + return LockscreenCredential.createNone(); + } + } + private void runSetPattern() { - byte[] oldBytes = mOld != null ? mOld.getBytes() : null; - mLockPatternUtils.saveLockPattern(stringToPattern(mNew), oldBytes, mCurrentUserId); + mLockPatternUtils.setLockCredential( + LockscreenCredential.createPattern(LockPatternUtils.byteArrayToPattern( + mNew.getBytes())), + getOldCredential(), + mCurrentUserId); getOutPrintWriter().println("Pattern set to '" + mNew + "'"); } private void runSetPassword() { - byte[] newBytes = mNew != null ? mNew.getBytes() : null; - byte[] oldBytes = mOld != null ? mOld.getBytes() : null; - mLockPatternUtils.saveLockPassword(newBytes, oldBytes, PASSWORD_QUALITY_ALPHABETIC, + mLockPatternUtils.setLockCredential(LockscreenCredential.createPassword(mNew), + getOldCredential(), mCurrentUserId); getOutPrintWriter().println("Password set to '" + mNew + "'"); } private void runSetPin() { - byte[] newBytes = mNew != null ? mNew.getBytes() : null; - byte[] oldBytes = mOld != null ? mOld.getBytes() : null; - mLockPatternUtils.saveLockPassword(newBytes, oldBytes, PASSWORD_QUALITY_NUMERIC, + mLockPatternUtils.setLockCredential(LockscreenCredential.createPin(mNew), + getOldCredential(), mCurrentUserId); getOutPrintWriter().println("Pin set to '" + mNew + "'"); } private void runClear() { - byte[] oldBytes = mOld != null ? mOld.getBytes() : null; - mLockPatternUtils.clearLock(oldBytes, mCurrentUserId); + mLockPatternUtils.setLockCredential(LockscreenCredential.createNone(), + getOldCredential(), + mCurrentUserId); getOutPrintWriter().println("Lock credential cleared"); } @@ -238,13 +252,8 @@ class LockSettingsShellCommand extends ShellCommand { } try { - final boolean result; - if (havePassword) { - byte[] passwordBytes = mOld != null ? mOld.getBytes() : null; - result = mLockPatternUtils.checkPassword(passwordBytes, mCurrentUserId); - } else { - result = mLockPatternUtils.checkPattern(stringToPattern(mOld), mCurrentUserId); - } + final boolean result = mLockPatternUtils.checkCredential(getOldCredential(), + mCurrentUserId, null); if (!result) { if (!mLockPatternUtils.isManagedProfileWithUnifiedChallenge(mCurrentUserId)) { mLockPatternUtils.reportFailedPasswordAttempt(mCurrentUserId); diff --git a/services/core/java/com/android/server/locksettings/LockSettingsStorage.java b/services/core/java/com/android/server/locksettings/LockSettingsStorage.java index 559461485042..f4cad634ac1a 100644 --- a/services/core/java/com/android/server/locksettings/LockSettingsStorage.java +++ b/services/core/java/com/android/server/locksettings/LockSettingsStorage.java @@ -95,8 +95,6 @@ class LockSettingsStorage { @VisibleForTesting public static class CredentialHash { - /** Deprecated private static final int VERSION_LEGACY = 0; */ - private static final int VERSION_GATEKEEPER = 1; private CredentialHash(byte[] hash, @CredentialType int type) { if (type != LockPatternUtils.CREDENTIAL_TYPE_NONE) { @@ -126,42 +124,6 @@ class LockSettingsStorage { byte[] hash; @CredentialType int type; - - public byte[] toBytes() { - try { - ByteArrayOutputStream os = new ByteArrayOutputStream(); - DataOutputStream dos = new DataOutputStream(os); - dos.write(VERSION_GATEKEEPER); - dos.write(type); - if (hash != null && hash.length > 0) { - dos.writeInt(hash.length); - dos.write(hash); - } else { - dos.writeInt(0); - } - dos.close(); - return os.toByteArray(); - } catch (IOException e) { - throw new IllegalStateException("Fail to serialze credential hash", e); - } - } - - public static CredentialHash fromBytes(byte[] bytes) { - try { - DataInputStream is = new DataInputStream(new ByteArrayInputStream(bytes)); - /* int version = */ is.read(); - int type = is.read(); - int hashSize = is.readInt(); - byte[] hash = null; - if (hashSize > 0) { - hash = new byte[hashSize]; - is.readFully(hash); - } - return new CredentialHash(hash, type); - } catch (IOException e) { - throw new IllegalStateException("Fail to deserialze credential hash", e); - } - } } public LockSettingsStorage(Context context) { diff --git a/services/core/java/com/android/server/media/MediaRoute2ProviderProxy.java b/services/core/java/com/android/server/media/MediaRoute2ProviderProxy.java index e753a7be99f6..9eac252f3966 100644 --- a/services/core/java/com/android/server/media/MediaRoute2ProviderProxy.java +++ b/services/core/java/com/android/server/media/MediaRoute2ProviderProxy.java @@ -93,7 +93,7 @@ final class MediaRoute2ProviderProxy implements ServiceConnection { public void unselectRoute(String packageName, String routeId) { if (mConnectionReady) { - mActiveConnection.unselectRotue(packageName, routeId); + mActiveConnection.unselectRoute(packageName, routeId); updateBinding(); } } @@ -105,6 +105,20 @@ final class MediaRoute2ProviderProxy implements ServiceConnection { } } + public void requestSetVolume(MediaRoute2Info route, int volume) { + if (mConnectionReady) { + mActiveConnection.requestSetVolume(route.getId(), volume); + updateBinding(); + } + } + + public void requestUpdateVolume(MediaRoute2Info route, int delta) { + if (mConnectionReady) { + mActiveConnection.requestUpdateVolume(route.getId(), delta); + updateBinding(); + } + } + @Nullable public MediaRoute2ProviderInfo getProviderInfo() { return mProviderInfo; @@ -324,7 +338,7 @@ final class MediaRoute2ProviderProxy implements ServiceConnection { } } - public void unselectRotue(String packageName, String routeId) { + public void unselectRoute(String packageName, String routeId) { try { mProvider.unselectRoute(packageName, routeId); } catch (RemoteException ex) { @@ -340,6 +354,22 @@ final class MediaRoute2ProviderProxy implements ServiceConnection { } } + public void requestSetVolume(String routeId, int volume) { + try { + mProvider.requestSetVolume(routeId, volume); + } catch (RemoteException ex) { + Slog.e(TAG, "Failed to deliver request to request set volume.", ex); + } + } + + public void requestUpdateVolume(String routeId, int delta) { + try { + mProvider.requestUpdateVolume(routeId, delta); + } catch (RemoteException ex) { + Slog.e(TAG, "Failed to deliver request to request update volume.", ex); + } + } + @Override public void binderDied() { mHandler.post(() -> onConnectionDied(Connection.this)); diff --git a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java index 668f2be158c6..74d59ac7b62a 100644 --- a/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java +++ b/services/core/java/com/android/server/media/MediaRouter2ServiceImpl.java @@ -198,6 +198,34 @@ class MediaRouter2ServiceImpl { } } + public void requestSetVolume2(IMediaRouter2Client client, MediaRoute2Info route, int volume) { + Objects.requireNonNull(client, "client must not be null"); + Objects.requireNonNull(route, "route must not be null"); + + final long token = Binder.clearCallingIdentity(); + try { + synchronized (mLock) { + requestSetVolumeLocked(client, route, volume); + } + } finally { + Binder.restoreCallingIdentity(token); + } + } + + public void requestUpdateVolume2(IMediaRouter2Client client, MediaRoute2Info route, int delta) { + Objects.requireNonNull(client, "client must not be null"); + Objects.requireNonNull(route, "route must not be null"); + + final long token = Binder.clearCallingIdentity(); + try { + synchronized (mLock) { + requestUpdateVolumeLocked(client, route, delta); + } + } finally { + Binder.restoreCallingIdentity(token); + } + } + public void selectClientRoute2(@NonNull IMediaRouter2Manager manager, String packageName, @Nullable MediaRoute2Info route) { final long token = Binder.clearCallingIdentity(); @@ -210,6 +238,37 @@ class MediaRouter2ServiceImpl { } } + public void requestSetVolume2Manager(IMediaRouter2Manager manager, + MediaRoute2Info route, int volume) { + Objects.requireNonNull(manager, "manager must not be null"); + Objects.requireNonNull(route, "route must not be null"); + + final long token = Binder.clearCallingIdentity(); + try { + synchronized (mLock) { + requestSetVolumeLocked(manager, route, volume); + } + } finally { + Binder.restoreCallingIdentity(token); + } + } + + public void requestUpdateVolume2Manager(IMediaRouter2Manager manager, + MediaRoute2Info route, int delta) { + Objects.requireNonNull(manager, "manager must not be null"); + Objects.requireNonNull(route, "route must not be null"); + + final long token = Binder.clearCallingIdentity(); + try { + synchronized (mLock) { + requestUpdateVolumeLocked(manager, route, delta); + } + } finally { + Binder.restoreCallingIdentity(token); + } + } + + public void registerClient(@NonNull IMediaRouterClient client, @NonNull String packageName) { Objects.requireNonNull(client, "client must not be null"); @@ -362,6 +421,30 @@ class MediaRouter2ServiceImpl { } } + private void requestSetVolumeLocked(IMediaRouter2Client client, MediaRoute2Info route, + int volume) { + final IBinder binder = client.asBinder(); + ClientRecord clientRecord = mAllClientRecords.get(binder); + + if (clientRecord != null) { + clientRecord.mUserRecord.mHandler.sendMessage( + obtainMessage(UserHandler::requestSetVolume, + clientRecord.mUserRecord.mHandler, route, volume)); + } + } + + private void requestUpdateVolumeLocked(IMediaRouter2Client client, MediaRoute2Info route, + int delta) { + final IBinder binder = client.asBinder(); + ClientRecord clientRecord = mAllClientRecords.get(binder); + + if (clientRecord != null) { + clientRecord.mUserRecord.mHandler.sendMessage( + obtainMessage(UserHandler::requestUpdateVolume, + clientRecord.mUserRecord.mHandler, route, delta)); + } + } + private void registerManagerLocked(IMediaRouter2Manager manager, int uid, int pid, String packageName, int userId, boolean trusted) { final IBinder binder = manager.asBinder(); @@ -424,6 +507,31 @@ class MediaRouter2ServiceImpl { } } + private void requestSetVolumeLocked(IMediaRouter2Manager manager, MediaRoute2Info route, + int volume) { + final IBinder binder = manager.asBinder(); + ManagerRecord managerRecord = mAllManagerRecords.get(binder); + + if (managerRecord != null) { + managerRecord.mUserRecord.mHandler.sendMessage( + obtainMessage(UserHandler::requestSetVolume, + managerRecord.mUserRecord.mHandler, route, volume)); + } + } + + private void requestUpdateVolumeLocked(IMediaRouter2Manager manager, MediaRoute2Info route, + int delta) { + final IBinder binder = manager.asBinder(); + ManagerRecord managerRecord = mAllManagerRecords.get(binder); + + if (managerRecord != null) { + managerRecord.mUserRecord.mHandler.sendMessage( + obtainMessage(UserHandler::requestUpdateVolume, + managerRecord.mUserRecord.mHandler, route, delta)); + } + } + + private void initializeUserLocked(UserRecord userRecord) { if (DEBUG) { Slog.d(TAG, userRecord + ": Initialized"); @@ -679,6 +787,20 @@ class MediaRouter2ServiceImpl { } } + private void requestSetVolume(MediaRoute2Info route, int volume) { + final MediaRoute2ProviderProxy provider = findProvider(route.getProviderId()); + if (provider != null) { + provider.requestSetVolume(route, volume); + } + } + + private void requestUpdateVolume(MediaRoute2Info route, int delta) { + final MediaRoute2ProviderProxy provider = findProvider(route.getProviderId()); + if (provider != null) { + provider.requestUpdateVolume(route, delta); + } + } + private void scheduleUpdateProviderInfos() { if (!mProviderInfosUpdateScheduled) { mProviderInfosUpdateScheduled = true; diff --git a/services/core/java/com/android/server/media/MediaRouterService.java b/services/core/java/com/android/server/media/MediaRouterService.java index 796a25d7e295..afd92f61b9d3 100644 --- a/services/core/java/com/android/server/media/MediaRouterService.java +++ b/services/core/java/com/android/server/media/MediaRouterService.java @@ -499,6 +499,32 @@ public final class MediaRouterService extends IMediaRouterService.Stub mService2.setControlCategories2(client, categories); } + // Binder call + @Override + public void requestSetVolume2(IMediaRouter2Client client, MediaRoute2Info route, int volume) { + mService2.requestSetVolume2(client, route, volume); + } + + // Binder call + @Override + public void requestUpdateVolume2(IMediaRouter2Client client, MediaRoute2Info route, int delta) { + mService2.requestUpdateVolume2(client, route, delta); + } + + // Binder call + @Override + public void requestSetVolume2Manager(IMediaRouter2Manager manager, + MediaRoute2Info route, int volume) { + mService2.requestSetVolume2Manager(manager, route, volume); + } + + // Binder call + @Override + public void requestUpdateVolume2Manager(IMediaRouter2Manager manager, + MediaRoute2Info route, int delta) { + mService2.requestUpdateVolume2Manager(manager, route, delta); + } + void restoreBluetoothA2dp() { try { boolean a2dpOn; diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java index 976a0c663101..812ce32a6030 100644 --- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java +++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java @@ -217,7 +217,6 @@ import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.messages.nano.SystemMessageProto.SystemMessage; import com.android.internal.notification.SystemNotificationChannels; -import com.android.internal.os.SomeArgs; import com.android.internal.telephony.PhoneConstants; import com.android.internal.util.ArrayUtils; import com.android.internal.util.ConcurrentUtils; @@ -385,6 +384,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { private static final int MSG_SUBSCRIPTION_OVERRIDE = 16; private static final int MSG_METERED_RESTRICTED_PACKAGES_CHANGED = 17; private static final int MSG_SET_NETWORK_TEMPLATE_ENABLED = 18; + private static final int MSG_SUBSCRIPTION_PLANS_CHANGED = 19; private static final int UID_MSG_STATE_CHANGED = 100; private static final int UID_MSG_GONE = 101; @@ -1509,6 +1509,11 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { latch.await(5, TimeUnit.SECONDS); } + @VisibleForTesting + Handler getHandlerForTesting() { + return mHandler; + } + /** * Update mobile policies with data cycle information from {@link CarrierConfigManager} * if necessary. @@ -3064,6 +3069,34 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { mContext.enforceCallingOrSelfPermission(MANAGE_SUBSCRIPTION_PLANS, TAG); } + private void enforceSubscriptionPlanValidity(SubscriptionPlan[] plans) { + // nothing to check if no plans + if (plans.length == 0) { + return; + } + + long applicableNetworkTypes = 0; + boolean allNetworks = false; + for (SubscriptionPlan plan : plans) { + if (plan.getNetworkTypes() == null) { + allNetworks = true; + } else { + if ((applicableNetworkTypes & plan.getNetworkTypesBitMask()) != 0) { + throw new IllegalArgumentException( + "Multiple subscription plans defined for a single network type."); + } else { + applicableNetworkTypes |= plan.getNetworkTypesBitMask(); + } + } + } + + // ensure at least one plan applies for every network type + if (!allNetworks) { + throw new IllegalArgumentException( + "No generic subscription plan that applies to all network types."); + } + } + @Override public SubscriptionPlan[] getSubscriptionPlans(int subId, String callingPackage) { enforceSubscriptionPlanAccess(subId, Binder.getCallingUid(), callingPackage); @@ -3228,6 +3261,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { @Override public void setSubscriptionPlans(int subId, SubscriptionPlan[] plans, String callingPackage) { enforceSubscriptionPlanAccess(subId, Binder.getCallingUid(), callingPackage); + enforceSubscriptionPlanValidity(plans); for (SubscriptionPlan plan : plans) { Preconditions.checkNotNull(plan); @@ -3256,6 +3290,8 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); intent.putExtra(SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX, subId); mContext.sendBroadcast(intent, android.Manifest.permission.MANAGE_SUBSCRIPTION_PLANS); + mHandler.sendMessage( + mHandler.obtainMessage(MSG_SUBSCRIPTION_PLANS_CHANGED, subId, 0, plans)); } finally { Binder.restoreCallingIdentity(token); } @@ -3282,7 +3318,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { @Override public void setSubscriptionOverride(int subId, int overrideMask, int overrideValue, - long networkTypeMask, long timeoutMillis, String callingPackage) { + long timeoutMillis, String callingPackage) { enforceSubscriptionPlanAccess(subId, Binder.getCallingUid(), callingPackage); // We can only override when carrier told us about plans @@ -3300,16 +3336,11 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { final boolean overrideEnabled = Settings.Global.getInt(mContext.getContentResolver(), NETPOLICY_OVERRIDE_ENABLED, 1) != 0; if (overrideEnabled || overrideValue == 0) { - SomeArgs args = SomeArgs.obtain(); - args.arg1 = subId; - args.arg2 = overrideMask; - args.arg3 = overrideValue; - args.arg4 = networkTypeMask; - mHandler.sendMessage(mHandler.obtainMessage(MSG_SUBSCRIPTION_OVERRIDE, args)); + mHandler.sendMessage(mHandler.obtainMessage(MSG_SUBSCRIPTION_OVERRIDE, + overrideMask, overrideValue, subId)); if (timeoutMillis > 0) { - args.arg3 = 0; - mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_SUBSCRIPTION_OVERRIDE, args), - timeoutMillis); + mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_SUBSCRIPTION_OVERRIDE, + overrideMask, 0, subId), timeoutMillis); } } } @@ -4445,11 +4476,20 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { } private void dispatchSubscriptionOverride(INetworkPolicyListener listener, int subId, - int overrideMask, int overrideValue, long networkTypeMask) { + int overrideMask, int overrideValue) { + if (listener != null) { + try { + listener.onSubscriptionOverride(subId, overrideMask, overrideValue); + } catch (RemoteException ignored) { + } + } + } + + private void dispatchSubscriptionPlansChanged(INetworkPolicyListener listener, int subId, + SubscriptionPlan[] plans) { if (listener != null) { try { - listener.onSubscriptionOverride(subId, overrideMask, overrideValue, - networkTypeMask); + listener.onSubscriptionPlansChanged(subId, plans); } catch (RemoteException ignored) { } } @@ -4550,16 +4590,13 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { return true; } case MSG_SUBSCRIPTION_OVERRIDE: { - final SomeArgs args = (SomeArgs) msg.obj; - final int subId = (int) args.arg1; - final int overrideMask = (int) args.arg2; - final int overrideValue = (int) args.arg3; - final long networkTypeMask = (long) args.arg4; + final int overrideMask = msg.arg1; + final int overrideValue = msg.arg2; + final int subId = (int) msg.obj; final int length = mListeners.beginBroadcast(); for (int i = 0; i < length; i++) { final INetworkPolicyListener listener = mListeners.getBroadcastItem(i); - dispatchSubscriptionOverride(listener, subId, overrideMask, overrideValue, - networkTypeMask); + dispatchSubscriptionOverride(listener, subId, overrideMask, overrideValue); } mListeners.finishBroadcast(); return true; @@ -4576,6 +4613,17 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { setNetworkTemplateEnabledInner(template, enabled); return true; } + case MSG_SUBSCRIPTION_PLANS_CHANGED: { + final SubscriptionPlan[] plans = (SubscriptionPlan[]) msg.obj; + final int subId = msg.arg1; + final int length = mListeners.beginBroadcast(); + for (int i = 0; i < length; i++) { + final INetworkPolicyListener listener = mListeners.getBroadcastItem(i); + dispatchSubscriptionPlansChanged(listener, subId, plans); + } + mListeners.finishBroadcast(); + return true; + } default: { return false; } diff --git a/services/core/java/com/android/server/notification/NotificationDelegate.java b/services/core/java/com/android/server/notification/NotificationDelegate.java index 61be1f5e559b..6f0ad33244e4 100644 --- a/services/core/java/com/android/server/notification/NotificationDelegate.java +++ b/services/core/java/com/android/server/notification/NotificationDelegate.java @@ -17,6 +17,7 @@ package com.android.server.notification; import android.app.Notification; +import android.net.Uri; import android.service.notification.NotificationStats; import com.android.internal.statusbar.NotificationVisibility; @@ -49,6 +50,12 @@ public interface NotificationDelegate { void onNotificationBubbleChanged(String key, boolean isBubble); /** + * Grant permission to read the specified URI to the package associated with the + * NotificationRecord associated with the given key. + */ + void grantInlineReplyUriPermission(String key, Uri uri, int callingUid); + + /** * Notifies that smart replies and actions have been added to the UI. */ void onNotificationSmartSuggestionsAdded(String key, int smartReplyCount, int smartActionCount, diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index 4457e9c312d8..ca979f8b13cc 100755 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -21,6 +21,7 @@ import static android.app.Notification.CATEGORY_CALL; import static android.app.Notification.FLAG_AUTOGROUP_SUMMARY; import static android.app.Notification.FLAG_BUBBLE; import static android.app.Notification.FLAG_FOREGROUND_SERVICE; +import static android.app.Notification.FLAG_INSISTENT; import static android.app.Notification.FLAG_NO_CLEAR; import static android.app.Notification.FLAG_ONGOING_EVENT; import static android.app.Notification.FLAG_ONLY_ALERT_ONCE; @@ -55,6 +56,7 @@ import static android.content.pm.PackageManager.MATCH_ALL; import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_AWARE; import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_UNAWARE; import static android.content.pm.PackageManager.PERMISSION_GRANTED; +import static android.media.AudioAttributes.USAGE_NOTIFICATION_RINGTONE; import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_CRITICAL; import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_NORMAL; import static android.os.UserHandle.USER_NULL; @@ -1154,6 +1156,56 @@ public class NotificationManagerService extends SystemService { } } } + + @Override + /** + * Grant permission to read the specified URI to the package specified in the + * NotificationRecord associated with the given key. The callingUid represents the UID of + * SystemUI from which this method is being called. + * + * For this to work, SystemUI must have permission to read the URI when running under the + * user associated with the NotificationRecord, and this grant will fail when trying + * to grant URI permissions across users. + */ + public void grantInlineReplyUriPermission(String key, Uri uri, int callingUid) { + synchronized (mNotificationLock) { + NotificationRecord r = mNotificationsByKey.get(key); + if (r != null) { + IBinder owner = r.permissionOwner; + if (owner == null) { + r.permissionOwner = mUgmInternal.newUriPermissionOwner("NOTIF:" + key); + owner = r.permissionOwner; + } + int uid = callingUid; + int userId = r.sbn.getUserId(); + if (userId == UserHandle.USER_ALL) { + userId = USER_SYSTEM; + } + if (UserHandle.getUserId(uid) != userId) { + try { + final String[] pkgs = mPackageManager.getPackagesForUid(callingUid); + if (pkgs == null) { + Log.e(TAG, "Cannot grant uri permission to unknown UID: " + + callingUid); + } + final String pkg = pkgs[0]; // Get the SystemUI package + // Find the UID for SystemUI for the correct user + uid = mPackageManager.getPackageUid(pkg, 0, userId); + } catch (RemoteException re) { + Log.e(TAG, "Cannot talk to package manager", re); + } + } + grantUriPermission(owner, uri, uid, r.sbn.getPackageName(), userId); + } else { + Log.w(TAG, "No record found for notification key:" + key); + + // TODO: figure out cancel story. I think it's: sysui needs to tell us + // whenever noitifications held by a lifetimextender go away + // IBinder owner = mUgmInternal.newUriPermissionOwner("InlineReply:" + key); + // pass in userId and package as well as key (key for logging purposes) + } + } + } }; @VisibleForTesting @@ -1183,7 +1235,7 @@ public class NotificationManagerService extends SystemService { } @GuardedBy("mNotificationLock") - private void clearSoundLocked() { + void clearSoundLocked() { mSoundNotificationKey = null; long identity = Binder.clearCallingIdentity(); try { @@ -1198,7 +1250,7 @@ public class NotificationManagerService extends SystemService { } @GuardedBy("mNotificationLock") - private void clearVibrateLocked() { + void clearVibrateLocked() { mVibrateNotificationKey = null; long identity = Binder.clearCallingIdentity(); try { @@ -4495,13 +4547,13 @@ public class NotificationManagerService extends SystemService { if (record != null && record.getAudioAttributes() != null) { if ((mListenerHints & HINT_HOST_DISABLE_NOTIFICATION_EFFECTS) != 0) { if (record.getAudioAttributes().getUsage() - != AudioAttributes.USAGE_VOICE_COMMUNICATION) { + != AudioAttributes.USAGE_NOTIFICATION_RINGTONE) { return "listenerNoti"; } } if ((mListenerHints & HINT_HOST_DISABLE_CALL_EFFECTS) != 0) { if (record.getAudioAttributes().getUsage() - == AudioAttributes.USAGE_VOICE_COMMUNICATION) { + == AudioAttributes.USAGE_NOTIFICATION_RINGTONE) { return "listenerCall"; } } @@ -6060,7 +6112,6 @@ public class NotificationManagerService extends SystemService { mIsAutomotive ? record.getImportance() > NotificationManager.IMPORTANCE_DEFAULT : record.getImportance() >= NotificationManager.IMPORTANCE_DEFAULT; - // Remember if this notification already owns the notification channels. boolean wasBeep = key != null && key.equals(mSoundNotificationKey); boolean wasBuzz = key != null && key.equals(mVibrateNotificationKey); @@ -6076,7 +6127,6 @@ public class NotificationManagerService extends SystemService { } if (aboveThreshold && isNotificationForCurrentUser(record)) { - if (mSystemReady && mAudioManager != null) { Uri soundUri = record.getSound(); hasValidSound = soundUri != null && !Uri.EMPTY.equals(soundUri); @@ -6091,7 +6141,6 @@ public class NotificationManagerService extends SystemService { vibration = mFallbackVibrationPattern; } hasValidVibrate = vibration != null; - boolean hasAudibleAlert = hasValidSound || hasValidVibrate; if (hasAudibleAlert && !shouldMuteNotificationLocked(record)) { if (!sentAccessibilityEvent) { @@ -6248,11 +6297,29 @@ public class NotificationManagerService extends SystemService { return true; } + // A looping ringtone, such as an incoming call is playing + if (isLoopingRingtoneNotification(mNotificationsByKey.get(mSoundNotificationKey)) + || isLoopingRingtoneNotification( + mNotificationsByKey.get(mVibrateNotificationKey))) { + return true; + } + + return false; + } + + @GuardedBy("mNotificationLock") + private boolean isLoopingRingtoneNotification(final NotificationRecord playingRecord) { + if (playingRecord != null) { + if (playingRecord.getAudioAttributes().getUsage() == USAGE_NOTIFICATION_RINGTONE + && (playingRecord.getNotification().flags & FLAG_INSISTENT) != 0) { + return true; + } + } return false; } private boolean playSound(final NotificationRecord record, Uri soundUri) { - boolean looping = (record.getNotification().flags & Notification.FLAG_INSISTENT) != 0; + boolean looping = (record.getNotification().flags & FLAG_INSISTENT) != 0; // play notifications if there is no user of exclusive audio focus // and the stream volume is not 0 (non-zero volume implies not silenced by SILENT or // VIBRATE ringer mode) @@ -6304,7 +6371,6 @@ public class NotificationManagerService extends SystemService { try { Thread.sleep(waitMs); } catch (InterruptedException e) { } - // Notifications might be canceled before it actually vibrates due to waitMs, // so need to check the notification still valide for vibrate. synchronized (mNotificationLock) { @@ -7012,7 +7078,6 @@ public class NotificationManagerService extends SystemService { private void grantUriPermission(IBinder owner, Uri uri, int sourceUid, String targetPkg, int targetUserId) { if (uri == null || !ContentResolver.SCHEME_CONTENT.equals(uri.getScheme())) return; - final long ident = Binder.clearCallingIdentity(); try { mUgm.grantUriPermissionFromOwner(owner, sourceUid, targetPkg, diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index ca577ee3c0a2..982ecbb9deae 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -974,124 +974,9 @@ public class PackageManagerService extends IPackageManager.Stub @Override public final boolean hasFeature(String feature) { return PackageManagerService.this.hasSystemFeature(feature, 0); } - - final List<PackageParser.Package> getStaticOverlayPackages( - Collection<PackageParser.Package> allPackages, String targetPackageName) { - if ("android".equals(targetPackageName)) { - // Static RROs targeting to "android", ie framework-res.apk, are already applied by - // native AssetManager. - return null; - } - - List<PackageParser.Package> overlayPackages = null; - for (PackageParser.Package p : allPackages) { - if (targetPackageName.equals(p.mOverlayTarget) && p.mOverlayIsStatic) { - if (overlayPackages == null) { - overlayPackages = new ArrayList<>(); - } - overlayPackages.add(p); - } - } - if (overlayPackages != null) { - Comparator<PackageParser.Package> cmp = - Comparator.comparingInt(p -> p.mOverlayPriority); - overlayPackages.sort(cmp); - } - return overlayPackages; - } - - final String[] getStaticOverlayPaths(List<PackageParser.Package> overlayPackages, - String targetPath) { - if (overlayPackages == null || overlayPackages.isEmpty()) { - return null; - } - List<String> overlayPathList = null; - for (PackageParser.Package overlayPackage : overlayPackages) { - if (targetPath == null) { - if (overlayPathList == null) { - overlayPathList = new ArrayList<>(); - } - overlayPathList.add(overlayPackage.baseCodePath); - continue; - } - - try { - // Creates idmaps for system to parse correctly the Android manifest of the - // target package. - // - // OverlayManagerService will update each of them with a correct gid from its - // target package app id. - mInstaller.idmap(targetPath, overlayPackage.baseCodePath, - UserHandle.getSharedAppGid( - UserHandle.getUserGid(UserHandle.USER_SYSTEM))); - if (overlayPathList == null) { - overlayPathList = new ArrayList<>(); - } - overlayPathList.add(overlayPackage.baseCodePath); - } catch (InstallerException e) { - Slog.e(TAG, "Failed to generate idmap for " + targetPath + " and " + - overlayPackage.baseCodePath); - } - } - return overlayPathList == null ? null : overlayPathList.toArray(new String[0]); - } - - String[] getStaticOverlayPaths(String targetPackageName, String targetPath) { - List<PackageParser.Package> overlayPackages; - synchronized (mInstallLock) { - synchronized (mLock) { - overlayPackages = getStaticOverlayPackages( - mPackages.values(), targetPackageName); - } - // It is safe to keep overlayPackages without holding mPackages because static overlay - // packages can't be uninstalled or disabled. - return getStaticOverlayPaths(overlayPackages, targetPath); - } - } - - @Override public final String[] getOverlayApks(String targetPackageName) { - return getStaticOverlayPaths(targetPackageName, null); - } - - @Override public final String[] getOverlayPaths(String targetPackageName, - String targetPath) { - return getStaticOverlayPaths(targetPackageName, targetPath); - } - } - - class ParallelPackageParserCallback extends PackageParserCallback { - List<PackageParser.Package> mOverlayPackages = null; - - void findStaticOverlayPackages() { - synchronized (mLock) { - for (PackageParser.Package p : mPackages.values()) { - if (p.mOverlayIsStatic) { - if (mOverlayPackages == null) { - mOverlayPackages = new ArrayList<>(); - } - mOverlayPackages.add(p); - } - } - } - } - - @Override - synchronized String[] getStaticOverlayPaths(String targetPackageName, String targetPath) { - // We can trust mOverlayPackages without holding mPackages because package uninstall - // can't happen while running parallel parsing. - // And we can call mInstaller inside getStaticOverlayPaths without holding mInstallLock - // because mInstallLock is held before running parallel parsing. - // Moreover holding mPackages or mInstallLock on each parsing thread causes dead-lock. - return mOverlayPackages == null ? null : - getStaticOverlayPaths( - getStaticOverlayPackages(mOverlayPackages, targetPackageName), - targetPath); - } } final PackageParser.Callback mPackageParserCallback = new PackageParserCallback(); - final ParallelPackageParserCallback mParallelPackageParserCallback = - new ParallelPackageParserCallback(); // Currently known shared libraries. final ArrayMap<String, LongSparseArray<SharedLibraryInfo>> mSharedLibraries = new ArrayMap<>(); @@ -1604,6 +1489,7 @@ public class PackageManagerService extends IPackageManager.Stub final @Nullable String mAppPredictionServicePackage; final @Nullable String mIncidentReportApproverPackage; final @Nullable String[] mTelephonyPackages; + final @Nullable String mWifiPackage; final @NonNull String mServicesSystemSharedLibraryPackageName; final @NonNull String mSharedSystemSharedLibraryPackageName; @@ -2807,8 +2693,6 @@ public class PackageManagerService extends IPackageManager.Stub systemScanFlags | partition.scanFlag, 0); } - mParallelPackageParserCallback.findStaticOverlayPackages(); - scanDirTracedLI(frameworkDir, systemParseFlags, systemScanFlags | SCAN_NO_DEX | SCAN_AS_PRIVILEGED, 0); if (!mPackages.containsKey("android")) { @@ -3061,6 +2945,7 @@ public class PackageManagerService extends IPackageManager.Stub mAppPredictionServicePackage = getAppPredictionServicePackageName(); mIncidentReportApproverPackage = getIncidentReportApproverPackageName(); mTelephonyPackages = getTelephonyPackageNames(); + mWifiPackage = mContext.getString(R.string.config_wifiPackage); // Now that we know all of the shared libraries, update all clients to have // the correct library paths. @@ -8469,7 +8354,7 @@ public class PackageManagerService extends IPackageManager.Stub } try (ParallelPackageParser parallelPackageParser = new ParallelPackageParser( mSeparateProcesses, mOnlyCore, mMetrics, mCacheDir, - mParallelPackageParserCallback)) { + mPackageParserCallback)) { // Submit files for parsing in parallel int fileCount = 0; for (File file : files) { @@ -10685,6 +10570,50 @@ public class PackageManagerService extends IPackageManager.Stub return changedAbiCodePath; } + /** + * Sets the enabled state of components configured through {@link SystemConfig}. + * This modifies the {@link PackageSetting} object. + **/ + static void configurePackageComponents(PackageParser.Package pkg) { + final ArrayMap<String, Boolean> componentsEnabledStates = SystemConfig.getInstance() + .getComponentsEnabledStates(pkg.packageName); + if (componentsEnabledStates == null) { + return; + } + + for (int i = pkg.activities.size() - 1; i >= 0; i--) { + final PackageParser.Activity component = pkg.activities.get(i); + final Boolean enabled = componentsEnabledStates.get(component.className); + if (enabled != null) { + component.info.enabled = enabled; + } + } + + for (int i = pkg.receivers.size() - 1; i >= 0; i--) { + final PackageParser.Activity component = pkg.receivers.get(i); + final Boolean enabled = componentsEnabledStates.get(component.className); + if (enabled != null) { + component.info.enabled = enabled; + } + } + + for (int i = pkg.providers.size() - 1; i >= 0; i--) { + final PackageParser.Provider component = pkg.providers.get(i); + final Boolean enabled = componentsEnabledStates.get(component.className); + if (enabled != null) { + component.info.enabled = enabled; + } + } + + for (int i = pkg.services.size() - 1; i >= 0; i--) { + final PackageParser.Service component = pkg.services.get(i); + final Boolean enabled = componentsEnabledStates.get(component.className); + if (enabled != null) { + component.info.enabled = enabled; + } + } + } + /** * Just scans the package without any side effects. @@ -10852,6 +10781,10 @@ public class PackageManagerService extends IPackageManager.Stub pkg.applicationInfo.initForUser(UserHandle.USER_SYSTEM); } + if (pkg.isSystem()) { + configurePackageComponents(pkg); + } + final String cpuAbiOverride = deriveAbiOverride(pkg.cpuAbiOverride, pkgSetting); if ((scanFlags & SCAN_NEW_INSTALL) == 0) { @@ -16067,10 +16000,6 @@ public class PackageManagerService extends IPackageManager.Stub * will be used to scan and reconcile the package. */ private static class PrepareResult { - public final int installReason; - public final String volumeUuid; - public final String installerPackageName; - public final UserHandle user; public final boolean replace; public final int scanFlags; public final int parseFlags; @@ -16079,24 +16008,16 @@ public class PackageManagerService extends IPackageManager.Stub public final PackageParser.Package packageToScan; public final boolean clearCodeCache; public final boolean system; - /* The original package name if it was changed during an update, otherwise {@code null}. */ - @Nullable - public final String renamedPackage; public final PackageFreezer freezer; public final PackageSetting originalPs; public final PackageSetting disabledPs; public final PackageSetting[] childPackageSettings; - private PrepareResult(int installReason, String volumeUuid, - String installerPackageName, UserHandle user, boolean replace, int scanFlags, + private PrepareResult(boolean replace, int scanFlags, int parseFlags, PackageParser.Package existingPackage, PackageParser.Package packageToScan, boolean clearCodeCache, boolean system, - String renamedPackage, PackageFreezer freezer, PackageSetting originalPs, + PackageFreezer freezer, PackageSetting originalPs, PackageSetting disabledPs, PackageSetting[] childPackageSettings) { - this.installReason = installReason; - this.volumeUuid = volumeUuid; - this.installerPackageName = installerPackageName; - this.user = user; this.replace = replace; this.scanFlags = scanFlags; this.parseFlags = parseFlags; @@ -16104,7 +16025,6 @@ public class PackageManagerService extends IPackageManager.Stub this.packageToScan = packageToScan; this.clearCodeCache = clearCodeCache; this.system = system; - this.renamedPackage = renamedPackage; this.freezer = freezer; this.originalPs = originalPs; this.disabledPs = disabledPs; @@ -16144,8 +16064,6 @@ public class PackageManagerService extends IPackageManager.Stub private PrepareResult preparePackageLI(InstallArgs args, PackageInstalledInfo res) throws PrepareFailure { final int installFlags = args.installFlags; - final String installerPackageName = args.installerPackageName; - final String volumeUuid = args.volumeUuid; final File tmpPackageFile = new File(args.getCodePath()); final boolean onExternal = args.volumeUuid != null; final boolean instantApp = ((installFlags & PackageManager.INSTALL_INSTANT_APP) != 0); @@ -16570,14 +16488,12 @@ public class PackageManagerService extends IPackageManager.Stub final PackageParser.Package existingPackage; String renamedPackage = null; boolean sysPkg = false; - String targetVolumeUuid = volumeUuid; int targetScanFlags = scanFlags; int targetParseFlags = parseFlags; final PackageSetting ps; final PackageSetting disabledPs; final PackageSetting[] childPackages; if (replace) { - targetVolumeUuid = null; if (pkg.applicationInfo.isStaticSharedLibrary()) { // Static libs have a synthetic package name containing the version // and cannot be updated as an update would get a new package name, @@ -16829,9 +16745,8 @@ public class PackageManagerService extends IPackageManager.Stub // we're passing the freezer back to be closed in a later phase of install shouldCloseFreezerBeforeReturn = false; - return new PrepareResult(args.installReason, targetVolumeUuid, installerPackageName, - args.user, replace, targetScanFlags, targetParseFlags, existingPackage, pkg, - replace /* clearCodeCache */, sysPkg, renamedPackage, freezer, + return new PrepareResult(replace, targetScanFlags, targetParseFlags, + existingPackage, pkg, replace /* clearCodeCache */, sysPkg, freezer, ps, disabledPs, childPackages); } finally { if (shouldCloseFreezerBeforeReturn) { @@ -22942,6 +22857,8 @@ public class PackageManagerService extends IPackageManager.Stub return new String[]{mAppPredictionServicePackage}; case PackageManagerInternal.PACKAGE_TELEPHONY: return mTelephonyPackages; + case PackageManagerInternal.PACKAGE_WIFI: + return new String[]{mWifiPackage}; } return ArrayUtils.emptyArray(String.class); } diff --git a/services/core/java/com/android/server/pm/permission/BasePermission.java b/services/core/java/com/android/server/pm/permission/BasePermission.java index c39dcfefb2e8..037912a66b3a 100644 --- a/services/core/java/com/android/server/pm/permission/BasePermission.java +++ b/services/core/java/com/android/server/pm/permission/BasePermission.java @@ -279,6 +279,9 @@ public final class BasePermission { public boolean isTelephony() { return (protectionLevel & PermissionInfo.PROTECTION_FLAG_TELEPHONY) != 0; } + public boolean isWifi() { + return (protectionLevel & PermissionInfo.PROTECTION_FLAG_WIFI) != 0; + } public void transfer(@NonNull String origPackageName, @NonNull String newPackageName) { if (!origPackageName.equals(sourcePackageName)) { diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java index 53cd530197a5..3f45b0bd9e77 100644 --- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java +++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java @@ -3282,6 +3282,13 @@ public class PermissionManagerService extends IPermissionManager.Stub { // Special permissions for the system telephony apps. allowed = true; } + if (!allowed && bp.isWifi() + && ArrayUtils.contains(mPackageManagerInt.getKnownPackageNames( + PackageManagerInternal.PACKAGE_WIFI, UserHandle.USER_SYSTEM), + pkg.packageName)) { + // Special permissions for the system wifi. + allowed = true; + } } return allowed; } diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java index c851cc69a732..2593c38180e2 100644 --- a/services/core/java/com/android/server/policy/PhoneWindowManager.java +++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java @@ -47,8 +47,8 @@ import static android.view.WindowManager.LayoutParams.LAST_SUB_WINDOW; import static android.view.WindowManager.LayoutParams.LAST_SYSTEM_WINDOW; import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY; import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_KEYGUARD; -import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS; import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_SYSTEM_ERROR; +import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS; import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING; @@ -2189,10 +2189,10 @@ public class PhoneWindowManager implements WindowManagerPolicy { default: // These are the windows that by default are shown only to the user that created // them. If this needs to be overridden, set - // {@link WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS} in + // {@link WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS} in // {@link WindowManager.LayoutParams}. Note that permission // {@link android.Manifest.permission.INTERNAL_SYSTEM_WINDOW} is required as well. - if ((attrs.privateFlags & PRIVATE_FLAG_SHOW_FOR_ALL_USERS) == 0) { + if ((attrs.privateFlags & SYSTEM_FLAG_SHOW_FOR_ALL_USERS) == 0) { return true; } break; @@ -2446,7 +2446,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { com.android.internal.R.styleable.Window_windowAnimationStyle, 0); params.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_FAKE_HARDWARE_ACCELERATED; - params.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS; + params.privateFlags |= WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS; if (!compatInfo.supportsScreen()) { params.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_COMPATIBLE_WINDOW; diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java index 3439d3841973..65bb2342d504 100644 --- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java +++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java @@ -29,6 +29,7 @@ import android.graphics.Rect; import android.hardware.biometrics.IBiometricServiceReceiverInternal; import android.hardware.display.DisplayManager; import android.hardware.display.DisplayManager.DisplayListener; +import android.net.Uri; import android.os.Binder; import android.os.Bundle; import android.os.Handler; @@ -1334,6 +1335,18 @@ public class StatusBarManagerService extends IStatusBarService.Stub implements D } @Override + public void grantInlineReplyUriPermission(String key, Uri uri) { + enforceStatusBarService(); + int callingUid = Binder.getCallingUid(); + long identity = Binder.clearCallingIdentity(); + try { + mNotificationDelegate.grantInlineReplyUriPermission(key, uri, callingUid); + } finally { + Binder.restoreCallingIdentity(identity); + } + } + + @Override public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err, String[] args, ShellCallback callback, ResultReceiver resultReceiver) { (new StatusBarShellCommand(this, mContext)).exec( diff --git a/services/core/java/com/android/server/wm/ActivityMetricsLaunchObserver.java b/services/core/java/com/android/server/wm/ActivityMetricsLaunchObserver.java index eff0f75466d9..c6b17e24b1de 100644 --- a/services/core/java/com/android/server/wm/ActivityMetricsLaunchObserver.java +++ b/services/core/java/com/android/server/wm/ActivityMetricsLaunchObserver.java @@ -39,8 +39,13 @@ import java.lang.annotation.RetentionPolicy; * If an activity is successfully started, the launch sequence's state will transition into * {@code STARTED} via {@link #onActivityLaunched}. This is a transient state. * - * It must then transition to either {@code CANCELLED} with {@link #onActivityLaunchCancelled} - * or into {@code FINISHED} with {@link #onActivityLaunchFinished}. These are terminal states. + * It must then transition to either {@code CANCELLED} with {@link #onActivityLaunchCancelled}, + * which is a terminal state or into {@code FINISHED} with {@link #onActivityLaunchFinished}. + * + * The {@code FINISHED} with {@link #onActivityLaunchFinished} then may transition to + * {@code FULLY_DRAWN} with {@link #onReportFullyDrawn}, which is a terminal state. + * Note this transition may not happen if the reportFullyDrawn event is not receivied, + * in which case {@code FINISHED} is terminal. * * Note that the {@code ActivityRecordProto} provided as a parameter to some state transitions isn't * necessarily the same within a single launch sequence: it is only the top-most activity at the @@ -51,15 +56,15 @@ import java.lang.annotation.RetentionPolicy; * until a subsequent transition into {@code INTENT_STARTED} initiates a new launch sequence. * * <pre> - * ┌⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯┐ ┌⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯┐ ╔══════════════════════════╗ - * ╴╴▶ ⋮ INTENT_STARTED ⋮ ──▶ ⋮ ACTIVITY_LAUNCHED ⋮ ──▶ ║ ACTIVITY_LAUNCH_FINISHED ║ - * └⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯┘ └⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯┘ ╚══════════════════════════╝ - * : : - * : : - * ▼ ▼ - * ╔════════════════╗ ╔═══════════════════════════╗ - * ║ INTENT_FAILED ║ ║ ACTIVITY_LAUNCH_CANCELLED ║ - * ╚════════════════╝ ╚═══════════════════════════╝ + * ┌⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯┐ ┌⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯┐ ┌--------------------------┐ + * ╴╴▶ INTENT_STARTED ──▶ ACTIVITY_LAUNCHED ──▶ ACTIVITY_LAUNCH_FINISHED + * └⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯┘ └⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯⋯┘ └--------------------------┘ + * : : : + * : : : + * ▼ ▼ ▼ + * ╔════════════════╗ ╔═══════════════════════════╗ ╔═══════════════════════════╗ + * ║ INTENT_FAILED ║ ║ ACTIVITY_LAUNCH_CANCELLED ║ ║ REPORT_FULLY_DRAWN ║ + * ╚════════════════╝ ╚═══════════════════════════╝ ╚═══════════════════════════╝ * </pre> */ public interface ActivityMetricsLaunchObserver { @@ -111,7 +116,7 @@ public interface ActivityMetricsLaunchObserver { * Multiple calls to this method cannot occur without first terminating the current * launch sequence. */ - public void onIntentStarted(@NonNull Intent intent); + public void onIntentStarted(@NonNull Intent intent, long timestampNanos); /** * Notifies the observer that the current launch sequence has failed to launch an activity. @@ -177,6 +182,9 @@ public interface ActivityMetricsLaunchObserver { * drawn for the first time: the top-most activity at the time is what's reported here. * * @param finalActivity the top-most activity whose windows were first to fully draw + * @param timestampNanos the timestamp of ActivityLaunchFinished event in nanoseconds. + * To compute the TotalTime duration, deduct the timestamp {@link #onIntentStarted} + * from {@code timestampNanos}. * * Multiple calls to this method cannot occur without first terminating the current * launch sequence. @@ -186,5 +194,22 @@ public interface ActivityMetricsLaunchObserver { * and only the latest activity that was top-most during first-frame drawn * is reported here. */ - public void onActivityLaunchFinished(@NonNull @ActivityRecordProto byte[] finalActivity); + public void onActivityLaunchFinished(@NonNull @ActivityRecordProto byte[] finalActivity, + long timestampNanos); + + /** + * Notifies the observer that the application self-reported itself as being fully drawn. + * + * @param activity the activity that triggers the ReportFullyDrawn event. + * @param timestampNanos the timestamp of ReportFullyDrawn event in nanoseconds. + * To compute the duration, deduct the deduct the timestamp {@link #onIntentStarted} + * from {@code timestampNanos}. + * + * @apiNote The behavior of ReportFullyDrawn mostly depends on the app. + * It is used as an accurate estimate of meanfully app startup time. + * This event may be missing for many apps. + */ + public void onReportFullyDrawn(@NonNull @ActivityRecordProto byte[] activity, + long timestampNanos); + } diff --git a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java index ef8c02004116..e6c6b12e18c6 100644 --- a/services/core/java/com/android/server/wm/ActivityMetricsLogger.java +++ b/services/core/java/com/android/server/wm/ActivityMetricsLogger.java @@ -92,6 +92,7 @@ import com.android.internal.logging.MetricsLogger; import com.android.internal.os.BackgroundThread; import com.android.internal.os.SomeArgs; import com.android.server.LocalServices; +import java.util.concurrent.TimeUnit; /** * Listens to activity launches, transitions, visibility changes and window drawn callbacks to @@ -132,8 +133,8 @@ class ActivityMetricsLogger { // set to INVALID_START_TIME in reset. // set to valid value in notifyActivityLaunching - private long mCurrentTransitionStartTime = INVALID_START_TIME; - private long mLastTransitionStartTime = INVALID_START_TIME; + private long mCurrentTransitionStartTimeNs = INVALID_START_TIME; + private long mLastTransitionStartTimeNs = INVALID_START_TIME; private int mCurrentTransitionDeviceUptime; private int mCurrentTransitionDelayMs; @@ -326,12 +327,12 @@ class ActivityMetricsLogger { intent)); } - if (mCurrentTransitionStartTime == INVALID_START_TIME) { + if (mCurrentTransitionStartTimeNs == INVALID_START_TIME) { - mCurrentTransitionStartTime = SystemClock.uptimeMillis(); - mLastTransitionStartTime = mCurrentTransitionStartTime; + mCurrentTransitionStartTimeNs = SystemClock.elapsedRealtimeNanos(); + mLastTransitionStartTimeNs = mCurrentTransitionStartTimeNs; - launchObserverNotifyIntentStarted(intent); + launchObserverNotifyIntentStarted(intent, mCurrentTransitionStartTimeNs); } } @@ -382,14 +383,15 @@ class ActivityMetricsLogger { ? launchedActivity.getWindowingMode() : WINDOWING_MODE_UNDEFINED; final WindowingModeTransitionInfo info = mWindowingModeTransitionInfo.get(windowingMode); - if (mCurrentTransitionStartTime == INVALID_START_TIME) { + if (mCurrentTransitionStartTimeNs == INVALID_START_TIME) { // No transition is active ignore this launch. return; } if (launchedActivity != null && launchedActivity.mDrawn) { // Launched activity is already visible. We cannot measure windows drawn delay. - reset(true /* abort */, info, "launched activity already visible"); + reset(true /* abort */, info, "launched activity already visible", + 0L /* timestampNs */); return; } @@ -407,7 +409,8 @@ class ActivityMetricsLogger { if ((!isLoggableResultCode(resultCode) || launchedActivity == null || !processSwitch || 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 */, info, "failed to launch or not a process switch"); + reset(true /* abort */, info, "failed to launch or not a process switch", + 0L /* timestampNs */); return; } else if (otherWindowModesLaunching) { // Don't log this windowing mode but continue with the other windowing modes. @@ -441,19 +444,20 @@ class ActivityMetricsLogger { * Notifies the tracker that all windows of the app have been drawn. */ WindowingModeTransitionInfoSnapshot notifyWindowsDrawn(@WindowingMode int windowingMode, - long timestamp) { + long timestampNs) { if (DEBUG_METRICS) Slog.i(TAG, "notifyWindowsDrawn windowingMode=" + windowingMode); final WindowingModeTransitionInfo info = mWindowingModeTransitionInfo.get(windowingMode); if (info == null || info.loggedWindowsDrawn) { return null; } - info.windowsDrawnDelayMs = calculateDelay(timestamp); + info.windowsDrawnDelayMs = calculateDelay(timestampNs); info.loggedWindowsDrawn = true; final WindowingModeTransitionInfoSnapshot infoSnapshot = new WindowingModeTransitionInfoSnapshot(info); if (allWindowsDrawn() && mLoggedTransitionStarting) { - reset(false /* abort */, info, "notifyWindowsDrawn - all windows drawn"); + reset(false /* abort */, info, "notifyWindowsDrawn - all windows drawn", + timestampNs /* timestampNs */); } return infoSnapshot; } @@ -476,7 +480,7 @@ class ActivityMetricsLogger { * @param windowingModeToReason A map from windowing mode to a reason integer, which must be on * of ActivityTaskManagerInternal.APP_TRANSITION_* reasons. */ - void notifyTransitionStarting(SparseIntArray windowingModeToReason, long timestamp) { + void notifyTransitionStarting(SparseIntArray windowingModeToReason, long timestampNs) { if (!isAnyTransitionActive() || mLoggedTransitionStarting) { // Ignore calls to this made after a reset and prior to notifyActivityLaunching. @@ -484,7 +488,7 @@ class ActivityMetricsLogger { return; } if (DEBUG_METRICS) Slog.i(TAG, "notifyTransitionStarting"); - mCurrentTransitionDelayMs = calculateDelay(timestamp); + mCurrentTransitionDelayMs = calculateDelay(timestampNs); mLoggedTransitionStarting = true; WindowingModeTransitionInfo foundInfo = null; @@ -501,7 +505,8 @@ class ActivityMetricsLogger { if (allWindowsDrawn()) { // abort metrics collection if we cannot find a matching transition. final boolean abortMetrics = foundInfo == null; - reset(abortMetrics, foundInfo, "notifyTransitionStarting - all windows drawn"); + reset(abortMetrics, foundInfo, "notifyTransitionStarting - all windows drawn", + timestampNs /* timestampNs */); } } @@ -567,7 +572,8 @@ class ActivityMetricsLogger { logAppTransitionCancel(info); mWindowingModeTransitionInfo.remove(r.getWindowingMode()); if (mWindowingModeTransitionInfo.size() == 0) { - reset(true /* abort */, info, "notifyVisibilityChanged to invisible"); + reset(true /* abort */, info, "notifyVisibilityChanged to invisible", + 0L /* timestampNs */); } } } @@ -598,12 +604,16 @@ class ActivityMetricsLogger { } private boolean isAnyTransitionActive() { - return mCurrentTransitionStartTime != INVALID_START_TIME + return mCurrentTransitionStartTimeNs != INVALID_START_TIME && mWindowingModeTransitionInfo.size() > 0; } - private void reset(boolean abort, WindowingModeTransitionInfo info, String cause) { - if (DEBUG_METRICS) Slog.i(TAG, "reset abort=" + abort + ",cause=" + cause); + private void reset(boolean abort, WindowingModeTransitionInfo info, String cause, + long timestampNs) { + if (DEBUG_METRICS) { + Slog.i(TAG, + "reset abort=" + abort + ",cause=" + cause + ",timestamp=" + timestampNs); + } if (!abort && isAnyTransitionActive()) { logAppTransitionMultiEvents(); } @@ -615,13 +625,13 @@ class ActivityMetricsLogger { if (abort) { launchObserverNotifyActivityLaunchCancelled(info); } else { - launchObserverNotifyActivityLaunchFinished(info); + launchObserverNotifyActivityLaunchFinished(info, timestampNs); } } else { launchObserverNotifyIntentFailed(); } - mCurrentTransitionStartTime = INVALID_START_TIME; + mCurrentTransitionStartTimeNs = INVALID_START_TIME; mCurrentTransitionDelayMs = INVALID_DELAY; mLoggedTransitionStarting = false; mWindowingModeTransitionInfo.clear(); @@ -629,12 +639,14 @@ class ActivityMetricsLogger { private int calculateCurrentDelay() { // Shouldn't take more than 25 days to launch an app, so int is fine here. - return (int) (SystemClock.uptimeMillis() - mCurrentTransitionStartTime); + return (int) TimeUnit.NANOSECONDS + .toMillis(SystemClock.elapsedRealtimeNanos() - mCurrentTransitionStartTimeNs); } - private int calculateDelay(long timestamp) { + private int calculateDelay(long timestampNs) { // Shouldn't take more than 25 days to launch an app, so int is fine here. - return (int) (timestamp - mCurrentTransitionStartTime); + return (int) TimeUnit.NANOSECONDS.toMillis(timestampNs - + mCurrentTransitionStartTimeNs); } private void logAppTransitionCancel(WindowingModeTransitionInfo info) { @@ -679,7 +691,7 @@ class ActivityMetricsLogger { // Take a snapshot of the transition info before sending it to the handler for logging. // This will avoid any races with other operations that modify the ActivityRecord. final WindowingModeTransitionInfoSnapshot infoSnapshot = - new WindowingModeTransitionInfoSnapshot(info); + new WindowingModeTransitionInfoSnapshot(info); final int currentTransitionDeviceUptime = mCurrentTransitionDeviceUptime; final int currentTransitionDelayMs = mCurrentTransitionDelayMs; BackgroundThread.getHandler().post(() -> logAppTransition( @@ -811,7 +823,9 @@ class ActivityMetricsLogger { final LogMaker builder = new LogMaker(APP_TRANSITION_REPORTED_DRAWN); builder.setPackageName(r.packageName); builder.addTaggedData(FIELD_CLASS_NAME, r.info.name); - long startupTimeMs = SystemClock.uptimeMillis() - mLastTransitionStartTime; + long currentTimestampNs = SystemClock.elapsedRealtimeNanos(); + long startupTimeMs = + TimeUnit.NANOSECONDS.toMillis(currentTimestampNs - mLastTransitionStartTimeNs); builder.addTaggedData(APP_TRANSITION_REPORTED_DRAWN_MS, startupTimeMs); builder.setType(restoredFromBundle ? TYPE_TRANSITION_REPORTED_DRAWN_WITH_BUNDLE @@ -837,6 +851,10 @@ class ActivityMetricsLogger { final WindowingModeTransitionInfoSnapshot infoSnapshot = new WindowingModeTransitionInfoSnapshot(info, r, (int) startupTimeMs); BackgroundThread.getHandler().post(() -> logAppFullyDrawn(infoSnapshot)); + + // Notify reportFullyDrawn event. + launchObserverNotifyReportFullyDrawn(r, currentTimestampNs); + return infoSnapshot; } @@ -1006,12 +1024,12 @@ class ActivityMetricsLogger { } /** Notify the {@link ActivityMetricsLaunchObserver} that a new launch sequence has begun. */ - private void launchObserverNotifyIntentStarted(Intent intent) { + private void launchObserverNotifyIntentStarted(Intent intent, long timestampNs) { Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "MetricsLogger:launchObserverNotifyIntentStarted"); // Beginning a launch is timing sensitive and so should be observed as soon as possible. - mLaunchObserver.onIntentStarted(intent); + mLaunchObserver.onIntentStarted(intent, timestampNs); Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); } @@ -1049,6 +1067,16 @@ class ActivityMetricsLogger { } /** + * Notifies the {@link ActivityMetricsLaunchObserver} the reportFullDrawn event. + */ + private void launchObserverNotifyReportFullyDrawn(ActivityRecord r, long timestampNs) { + Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, + "MetricsLogger:launchObserverNotifyReportFullyDrawn"); + mLaunchObserver.onReportFullyDrawn(convertActivityRecordToProto(r), timestampNs); + Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); + } + + /** * Notify the {@link ActivityMetricsLaunchObserver} that the current launch sequence is * cancelled. */ @@ -1068,12 +1096,14 @@ class ActivityMetricsLogger { * Notify the {@link ActivityMetricsLaunchObserver} that the current launch sequence's activity * has fully finished (successfully). */ - private void launchObserverNotifyActivityLaunchFinished(WindowingModeTransitionInfo info) { + private void launchObserverNotifyActivityLaunchFinished(WindowingModeTransitionInfo info, + long timestampNs) { Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "MetricsLogger:launchObserverNotifyActivityLaunchFinished"); - mLaunchObserver.onActivityLaunchFinished( - convertActivityRecordToProto(info.launchedActivity)); + mLaunchObserver + .onActivityLaunchFinished(convertActivityRecordToProto(info.launchedActivity), + timestampNs); Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); } diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index 16e617c626db..76a551fffa34 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -144,10 +144,10 @@ import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLAS import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_FREE_RESIZE; import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_NONE; import static com.android.server.wm.ActivityTaskManagerService.RELAUNCH_REASON_WINDOWING_MODE_RESIZE; +import static com.android.server.wm.ActivityTaskManagerService.getInputDispatchingTimeoutLocked; import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_ADD_REMOVE; import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_ORIENTATION; import static com.android.server.wm.ProtoLogGroup.WM_DEBUG_STARTING_WINDOW; -import static com.android.server.wm.ActivityTaskManagerService.getInputDispatchingTimeoutLocked; import static com.android.server.wm.TaskPersister.DEBUG; import static com.android.server.wm.TaskPersister.IMAGE_EXTENSION; import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; @@ -318,6 +318,7 @@ final class ActivityRecord extends AppWindowToken { ArrayList<ResultInfo> results; // pending ActivityResult objs we have received HashSet<WeakReference<PendingIntentRecord>> pendingResults; // all pending intents for this act ArrayList<ReferrerIntent> newIntents; // any pending new intents for single-top mode + Intent mLastNewIntent; // the last new intent we delivered to client ActivityOptions pendingOptions; // most recently given options ActivityOptions returningOptions; // options that are coming back via convertToTranslucent AppTimeTracker appTimeTracker; // set if we are tracking the time in this app/task/activity @@ -2844,6 +2845,9 @@ final class ActivityRecord extends AppWindowToken { } idle = false; results = null; + if (newIntents != null && newIntents.size() > 0) { + mLastNewIntent = newIntents.get(newIntents.size() - 1); + } newIntents = null; stopped = false; @@ -3102,7 +3106,7 @@ final class ActivityRecord extends AppWindowToken { void reportFullyDrawnLocked(boolean restoredFromBundle) { final WindowingModeTransitionInfoSnapshot info = mStackSupervisor - .getActivityMetricsLogger().logAppTransitionReportedDrawn(this, restoredFromBundle); + .getActivityMetricsLogger().logAppTransitionReportedDrawn(this, restoredFromBundle); if (info != null) { mStackSupervisor.reportActivityLaunchedLocked(false /* timeout */, this, info.windowsFullyDrawnDelayMs, info.getLaunchState()); @@ -3110,13 +3114,13 @@ final class ActivityRecord extends AppWindowToken { } /** Called when the windows associated app window container are drawn. */ - void onWindowsDrawn(boolean drawn, long timestamp) { + void onWindowsDrawn(boolean drawn, long timestampNs) { mDrawn = drawn; if (!drawn) { return; } final WindowingModeTransitionInfoSnapshot info = mStackSupervisor - .getActivityMetricsLogger().notifyWindowsDrawn(getWindowingMode(), timestamp); + .getActivityMetricsLogger().notifyWindowsDrawn(getWindowingMode(), timestampNs); final int windowsDrawnDelayMs = info != null ? info.windowsDrawnDelayMs : INVALID_DELAY; final @LaunchState int launchState = info != null ? info.getLaunchState() : -1; mStackSupervisor.reportActivityLaunchedLocked(false /* timeout */, this, @@ -4198,12 +4202,19 @@ final class ActivityRecord extends AppWindowToken { return true; } - // Restrict task snapshot starting window to launcher start, or there is no intent at all - // (eg. task being brought to front). If the intent is something else, likely the app is - // going to show some specific page or view, instead of what's left last time. + // Restrict task snapshot starting window to launcher start, or is same as the last + // delivered intent, or there is no intent at all (eg. task being brought to front). If + // the intent is something else, likely the app is going to show some specific page or + // view, instead of what's left last time. for (int i = newIntents.size() - 1; i >= 0; i--) { final Intent intent = newIntents.get(i); - if (intent != null && !ActivityRecord.isMainIntent(intent)) { + if (intent == null || ActivityRecord.isMainIntent(intent)) { + continue; + } + + final boolean sameIntent = mLastNewIntent != null ? mLastNewIntent.filterEquals(intent) + : this.intent.filterEquals(intent); + if (!sameIntent || intent.getExtras() != null) { return false; } } diff --git a/services/core/java/com/android/server/wm/ActivityStack.java b/services/core/java/com/android/server/wm/ActivityStack.java index 11c1e36ce5ce..41c1e4e7fcc5 100644 --- a/services/core/java/com/android/server/wm/ActivityStack.java +++ b/services/core/java/com/android/server/wm/ActivityStack.java @@ -33,7 +33,7 @@ import static android.app.WindowConfiguration.windowingModeToString; import static android.content.pm.ActivityInfo.CONFIG_SCREEN_LAYOUT; import static android.content.pm.ActivityInfo.FLAG_RESUME_WHILE_PAUSING; import static android.content.pm.ActivityInfo.FLAG_SHOW_FOR_ALL_USERS; -import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER; +import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER; import static android.view.Display.DEFAULT_DISPLAY; import static android.view.Display.FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD; import static android.view.Display.INVALID_DISPLAY; @@ -4342,7 +4342,7 @@ class ActivityStack extends ConfigurationContainer { return; } - Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "stack.resize_" + mStackId); + Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "stack.resize_" + mStackId); mService.deferWindowLayout(); try { // Update override configurations of all tasks in the stack. @@ -4368,7 +4368,7 @@ class ActivityStack extends ConfigurationContainer { } } finally { mService.continueWindowLayout(); - Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER); + Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); } } diff --git a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java index a262f160fee2..7753f572e3bc 100644 --- a/services/core/java/com/android/server/wm/ActivityStackSupervisor.java +++ b/services/core/java/com/android/server/wm/ActivityStackSupervisor.java @@ -44,7 +44,7 @@ import static android.graphics.Rect.copyOrNull; import static android.os.PowerManager.PARTIAL_WAKE_LOCK; import static android.os.Process.INVALID_UID; import static android.os.Process.SYSTEM_UID; -import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER; +import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER; import static android.view.Display.DEFAULT_DISPLAY; import static android.view.Display.INVALID_DISPLAY; import static android.view.Display.TYPE_VIRTUAL; @@ -690,7 +690,7 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks { ResolveInfo resolveIntent(Intent intent, String resolvedType, int userId, int flags, int filterCallingUid) { try { - Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "resolveIntent"); + Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "resolveIntent"); int modifiedFlags = flags | PackageManager.MATCH_DEFAULT_ONLY | ActivityManagerService.STOCK_PM_FLAGS; if (intent.isWebIntent() @@ -711,7 +711,7 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks { Binder.restoreCallingIdentity(token); } } finally { - Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER); + Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); } } @@ -1629,7 +1629,7 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks { mPendingTempOtherTaskInsetBounds = copyOrNull(tempOtherTaskInsetBounds); } - Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "am.resizeDockedStack"); + Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "resizeDockedStack"); mService.deferWindowLayout(); try { // Don't allow re-entry while resizing. E.g. due to docked stack detaching. @@ -1695,7 +1695,7 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks { } finally { mAllowDockedStackResize = true; mService.continueWindowLayout(); - Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER); + Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); } } @@ -1717,7 +1717,7 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks { return; } - Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "am.resizePinnedStack"); + Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "resizePinnedStack"); mService.deferWindowLayout(); try { Rect insetBounds = null; @@ -1739,7 +1739,7 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks { !DEFER_RESUME); } finally { mService.continueWindowLayout(); - Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER); + Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); } } diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java index b68ba7cc7961..1a8000636974 100644 --- a/services/core/java/com/android/server/wm/ActivityStarter.java +++ b/services/core/java/com/android/server/wm/ActivityStarter.java @@ -770,13 +770,13 @@ class ActivityStarter { boolean restrictedBgActivity = false; if (!abort) { try { - Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, + Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "shouldAbortBackgroundActivityStart"); restrictedBgActivity = shouldAbortBackgroundActivityStart(callingUid, callingPid, callingPackage, realCallingUid, realCallingPid, callerApp, originatingPendingIntent, allowBackgroundActivityStart, intent); } finally { - Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); + Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER); } } @@ -1401,41 +1401,12 @@ class ActivityStarter { final ActivityStack startedActivityStack; try { mService.deferWindowLayout(); - result = startActivityUnchecked(r, sourceRecord, voiceSession, voiceInteractor, + Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "startActivityInner"); + result = startActivityInner(r, sourceRecord, voiceSession, voiceInteractor, startFlags, doResume, options, inTask, outActivity, restrictedBgActivity); } finally { - final ActivityStack currentStack = r.getActivityStack(); - startedActivityStack = currentStack != null ? currentStack : mTargetStack; - - if (ActivityManager.isStartResultSuccessful(result)) { - if (startedActivityStack != null) { - // If there is no state change (e.g. a resumed activity is reparented to - // top of another display) to trigger a visibility/configuration checking, - // we have to update the configuration for changing to different display. - final ActivityRecord currentTop = - startedActivityStack.topRunningActivityLocked(); - if (currentTop != null && currentTop.shouldUpdateConfigForDisplayChanged()) { - mRootActivityContainer.ensureVisibilityAndConfig( - currentTop, currentTop.getDisplayId(), - true /* markFrozenIfConfigChanged */, false /* deferResume */); - } - } - } else { - // If we are not able to proceed, disassociate the activity from the task. - // Leaving an activity in an incomplete state can lead to issues, such as - // performing operations without a window container. - final ActivityStack stack = mStartActivity.getActivityStack(); - if (stack != null) { - mStartActivity.finishIfPossible("startActivity", true /* oomAdj */); - } - - // Stack should also be detached from display and be removed if it's empty. - if (startedActivityStack != null && startedActivityStack.isAttached() - && startedActivityStack.numActivities() == 0 - && !startedActivityStack.isActivityTypeHome()) { - startedActivityStack.remove(); - } - } + Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER); + startedActivityStack = handleStartResult(r, result); mService.continueWindowLayout(); } @@ -1445,6 +1416,49 @@ class ActivityStarter { } /** + * If the start result is success, ensure that the configuration of the started activity matches + * the current display. Otherwise clean up unassociated containers to avoid leakage. + * + * @return the stack where the successful started activity resides. + */ + private @Nullable ActivityStack handleStartResult(@NonNull ActivityRecord started, int result) { + final ActivityStack currentStack = started.getActivityStack(); + ActivityStack startedActivityStack = currentStack != null ? currentStack : mTargetStack; + + if (ActivityManager.isStartResultSuccessful(result)) { + if (startedActivityStack != null) { + // If there is no state change (e.g. a resumed activity is reparented to top of + // another display) to trigger a visibility/configuration checking, we have to + // update the configuration for changing to different display. + final ActivityRecord currentTop = startedActivityStack.topRunningActivityLocked(); + if (currentTop != null && currentTop.shouldUpdateConfigForDisplayChanged()) { + mRootActivityContainer.ensureVisibilityAndConfig( + currentTop, currentTop.getDisplayId(), + true /* markFrozenIfConfigChanged */, false /* deferResume */); + } + } + return startedActivityStack; + } + + // If we are not able to proceed, disassociate the activity from the task. Leaving an + // activity in an incomplete state can lead to issues, such as performing operations + // without a window container. + final ActivityStack stack = mStartActivity.getActivityStack(); + if (stack != null) { + mStartActivity.finishIfPossible("startActivity", true /* oomAdj */); + } + + // Stack should also be detached from display and be removed if it's empty. + if (startedActivityStack != null && startedActivityStack.isAttached() + && startedActivityStack.numActivities() == 0 + && !startedActivityStack.isActivityTypeHome()) { + startedActivityStack.remove(); + startedActivityStack = null; + } + return startedActivityStack; + } + + /** * Return true if background activity is really aborted. * * TODO(b/131748165): Refactor the logic so we don't need to call this method everywhere. @@ -1469,7 +1483,7 @@ class ActivityStarter { } // Note: This method should only be called from {@link startActivity}. - private int startActivityUnchecked(final ActivityRecord r, ActivityRecord sourceRecord, + private int startActivityInner(final ActivityRecord r, ActivityRecord sourceRecord, IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor, int startFlags, boolean doResume, ActivityOptions options, TaskRecord inTask, ActivityRecord[] outActivity, boolean restrictedBgActivity) { @@ -1592,7 +1606,7 @@ class ActivityStarter { // accordingly. if (mTargetStack.isFocusable() && !mRootActivityContainer.isTopDisplayFocusedStack(mTargetStack)) { - mTargetStack.moveToFront("startActivityUnchecked"); + mTargetStack.moveToFront("startActivityInner"); } mRootActivityContainer.resumeFocusedStacksTopActivities( mTargetStack, mStartActivity, mOptions); @@ -1873,7 +1887,7 @@ class ActivityStarter { mTargetStack = computeStackFocus(mSourceRecord, false /* newTask */, mLaunchFlags, mOptions); mTargetStack.addTask(targetTask, - !mLaunchTaskBehind /* toTop */, "startActivityUnchecked"); + !mLaunchTaskBehind /* toTop */, "complyActivityFlags"); } } } else if ((mLaunchFlags & FLAG_ACTIVITY_CLEAR_TOP) == 0 && !mAddingToTask diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java index ab35652eb525..0488a3b7065b 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java +++ b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java @@ -159,10 +159,10 @@ public abstract class ActivityTaskManagerInternal { * * @param reasons A map from windowing mode to a reason integer why the transition was started, * which must be one of the APP_TRANSITION_* values. - * @param timestamp The time at which the app transition started in - * {@link SystemClock#uptimeMillis()} timebase. + * @param timestampNs The time at which the app transition started in + * {@link SystemClock#elapsedRealtimeNs()} ()} timebase. */ - public abstract void notifyAppTransitionStarting(SparseIntArray reasons, long timestamp); + public abstract void notifyAppTransitionStarting(SparseIntArray reasons, long timestampNs); /** * Callback for window manager to let activity manager know that the app transition was diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java index 7ce3e794b8aa..20113a68fdc6 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java +++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java @@ -52,7 +52,7 @@ import static android.os.FactoryTest.FACTORY_TEST_LOW_LEVEL; import static android.os.FactoryTest.FACTORY_TEST_OFF; import static android.os.Process.FIRST_APPLICATION_UID; import static android.os.Process.SYSTEM_UID; -import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER; +import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER; import static android.provider.Settings.Global.DEVELOPMENT_ENABLE_FREEFORM_WINDOWS_SUPPORT; import static android.provider.Settings.Global.DEVELOPMENT_FORCE_RESIZABLE_ACTIVITIES; import static android.provider.Settings.Global.DEVELOPMENT_FORCE_RTL; @@ -1606,6 +1606,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { } final long origId = Binder.clearCallingIdentity(); + Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "finishActivity"); try { boolean res; final boolean finishWithRootActivity = @@ -1633,6 +1634,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { } return res; } finally { + Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); Binder.restoreCallingIdentity(origId); } } @@ -1667,6 +1669,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { try { WindowProcessController proc = null; synchronized (mGlobalLock) { + Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "activityIdle"); ActivityStack stack = ActivityRecord.getStackLocked(token); if (stack == null) { return; @@ -1681,6 +1684,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { } } } finally { + Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); Binder.restoreCallingIdentity(origId); } } @@ -1707,10 +1711,12 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { public final void activityPaused(IBinder token) { final long origId = Binder.clearCallingIdentity(); synchronized (mGlobalLock) { + Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "activityPaused"); ActivityStack stack = ActivityRecord.getStackLocked(token); if (stack != null) { stack.activityPausedLocked(token, false); } + Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); } Binder.restoreCallingIdentity(origId); } @@ -1731,6 +1737,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { int restartingUid = 0; final ActivityRecord r; synchronized (mGlobalLock) { + Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "activityStopped"); r = ActivityRecord.isInStackLocked(token); if (r != null) { if (r.attachedToProcess() @@ -1742,6 +1749,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { } r.activityStoppedLocked(icicle, persistentState, description); } + Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); } if (restartingName != null) { @@ -1763,12 +1771,14 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { if (DEBUG_SWITCH) Slog.v(TAG_SWITCH, "ACTIVITY DESTROYED: " + token); synchronized (mGlobalLock) { final long origId = Binder.clearCallingIdentity(); + Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "activityDestroyed"); try { final ActivityRecord activity = ActivityRecord.forTokenLocked(token); if (activity != null) { activity.destroyed("activityDestroyed"); } } finally { + Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); Binder.restoreCallingIdentity(origId); } } @@ -5506,8 +5516,8 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { void startProcessAsync(ActivityRecord activity, boolean knownToBeDead, boolean isTop, String hostingType) { try { - if (Trace.isTagEnabled(TRACE_TAG_ACTIVITY_MANAGER)) { - Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "dispatchingStartProcess:" + if (Trace.isTagEnabled(TRACE_TAG_WINDOW_MANAGER)) { + Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "dispatchingStartProcess:" + activity.processName); } // Post message to start process to avoid possible deadlock of calling into AMS with the @@ -5517,7 +5527,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { isTop, hostingType, activity.intent.getComponent()); mH.sendMessage(m); } finally { - Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER); + Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); } } @@ -5670,11 +5680,11 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { private void updateResumedAppTrace(@Nullable ActivityRecord resumed) { if (mTracedResumedActivity != null) { - Trace.asyncTraceEnd(TRACE_TAG_ACTIVITY_MANAGER, + Trace.asyncTraceEnd(TRACE_TAG_WINDOW_MANAGER, constructResumedTraceName(mTracedResumedActivity.packageName), 0); } if (resumed != null) { - Trace.asyncTraceBegin(TRACE_TAG_ACTIVITY_MANAGER, + Trace.asyncTraceBegin(TRACE_TAG_WINDOW_MANAGER, constructResumedTraceName(resumed.packageName), 0); } mTracedResumedActivity = resumed; @@ -6002,10 +6012,10 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { @Override public void notifyAppTransitionStarting(SparseIntArray reasons, - long timestamp) { + long timestampNs) { synchronized (mGlobalLock) { mStackSupervisor.getActivityMetricsLogger().notifyTransitionStarting( - reasons, timestamp); + reasons, timestampNs); } } @@ -6773,7 +6783,14 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { @Override public boolean attachApplication(WindowProcessController wpc) throws RemoteException { synchronized (mGlobalLockWithoutBoost) { - return mRootActivityContainer.attachApplication(wpc); + if (Trace.isTagEnabled(TRACE_TAG_WINDOW_MANAGER)) { + Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "attachApplication:" + wpc.mName); + } + try { + return mRootActivityContainer.attachApplication(wpc); + } finally { + Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); + } } } diff --git a/services/core/java/com/android/server/wm/AppTransitionController.java b/services/core/java/com/android/server/wm/AppTransitionController.java index 894dfd4065a4..0c07e15b9da5 100644 --- a/services/core/java/com/android/server/wm/AppTransitionController.java +++ b/services/core/java/com/android/server/wm/AppTransitionController.java @@ -209,7 +209,7 @@ public class AppTransitionController { mDisplayContent.computeImeTarget(true /* updateImeTarget */); mService.mAtmInternal.notifyAppTransitionStarting(mTempTransitionReasons.clone(), - SystemClock.uptimeMillis()); + SystemClock.elapsedRealtimeNanos()); if (transit == TRANSIT_SHOW_SINGLE_TASK_DISPLAY) { mService.mAnimator.addAfterPrepareSurfacesRunnable(() -> { diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java index 80a295d8f0ad..e56fdd244d4f 100644 --- a/services/core/java/com/android/server/wm/AppWindowToken.java +++ b/services/core/java/com/android/server/wm/AppWindowToken.java @@ -512,7 +512,7 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree if (DEBUG_VISIBILITY) Slog.v(TAG, "VIS " + this + ": interesting=" + numInteresting + " visible=" + numVisible); if (nowDrawn != reportedDrawn) { - onWindowsDrawn(nowDrawn, SystemClock.uptimeMillis()); + onWindowsDrawn(nowDrawn, SystemClock.elapsedRealtimeNanos()); reportedDrawn = nowDrawn; } if (nowVisible != reportedVisible) { @@ -2283,7 +2283,7 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree } } - private final Runnable mAddStartingWindow = new Runnable() { + private class AddStartingWindow implements Runnable { @Override public void run() { @@ -2343,7 +2343,9 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree AppWindowToken.this); } } - }; + } + + private final AddStartingWindow mAddStartingWindow = new AddStartingWindow(); private int getStartingWindowType(boolean newTask, boolean taskSwitch, boolean processRunning, boolean allowTaskSnapshot, boolean activityCreated, boolean fromRecents, diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java index 150ae7906510..60e9819ecbcf 100644 --- a/services/core/java/com/android/server/wm/DisplayPolicy.java +++ b/services/core/java/com/android/server/wm/DisplayPolicy.java @@ -196,6 +196,11 @@ public class DisplayPolicy { // Nav bar is never forced opaque. private static final int NAV_BAR_FORCE_TRANSPARENT = 2; + /** Don't apply window animation (see {@link #selectAnimation}). */ + static final int ANIMATION_NONE = -1; + /** Use the transit animation in style resource (see {@link #selectAnimation}). */ + static final int ANIMATION_STYLEABLE = 0; + /** * These are the system UI flags that, when changing, can cause the layout * of the screen to change. @@ -1036,9 +1041,9 @@ public class DisplayPolicy { } /** - * Control the animation to run when a window's state changes. Return a - * non-0 number to force the animation to a specific resource ID, or 0 - * to use the default animation. + * Control the animation to run when a window's state changes. Return a positive number to + * force the animation to a specific resource ID, {@link #ANIMATION_STYLEABLE} to use the + * style resource defining the animation, or {@link #ANIMATION_NONE} for no animation. * * @param win The window that is changing. * @param transit What is happening to the window: @@ -1047,9 +1052,9 @@ public class DisplayPolicy { * {@link com.android.server.policy.WindowManagerPolicy#TRANSIT_SHOW}, or * {@link com.android.server.policy.WindowManagerPolicy#TRANSIT_HIDE}. * - * @return Resource ID of the actual animation to use, or 0 for none. + * @return Resource ID of the actual animation to use, or {@link #ANIMATION_NONE} for none. */ - public int selectAnimationLw(WindowState win, int transit) { + int selectAnimation(WindowState win, int transit) { if (DEBUG_ANIM) Slog.i(TAG, "selectAnimation in " + win + ": transit=" + transit); if (win == mStatusBar) { @@ -1057,7 +1062,7 @@ public class DisplayPolicy { final boolean expanded = win.getAttrs().height == MATCH_PARENT && win.getAttrs().width == MATCH_PARENT; if (isKeyguard || expanded) { - return -1; + return ANIMATION_NONE; } if (transit == TRANSIT_EXIT || transit == TRANSIT_HIDE) { @@ -1068,7 +1073,7 @@ public class DisplayPolicy { } } else if (win == mNavigationBar) { if (win.getAttrs().windowAnimations != 0) { - return 0; + return ANIMATION_STYLEABLE; } // This can be on either the bottom or the right or the left. if (mNavigationBarPosition == NAV_BAR_BOTTOM) { @@ -1101,7 +1106,7 @@ public class DisplayPolicy { } } } else if (win.getAttrs().type == TYPE_DOCK_DIVIDER) { - return selectDockedDividerAnimationLw(win, transit); + return selectDockedDividerAnimation(win, transit); } if (transit == TRANSIT_PREVIEW_DONE) { @@ -1115,13 +1120,13 @@ public class DisplayPolicy { // is shown. We don't want an animation on the dream, because // we need it shown immediately with the keyguard animating away // to reveal it. - return -1; + return ANIMATION_NONE; } - return 0; + return ANIMATION_STYLEABLE; } - private int selectDockedDividerAnimationLw(WindowState win, int transit) { + private int selectDockedDividerAnimation(WindowState win, int transit) { int insets = mDisplayContent.getDockedDividerController().getContentInsets(); // If the divider is behind the navigation bar, don't animate. @@ -1140,14 +1145,14 @@ public class DisplayPolicy { || frame.bottom + insets >= win.getDisplayFrameLw().bottom); final boolean offscreen = offscreenLandscape || offscreenPortrait; if (behindNavBar || offscreen) { - return 0; + return ANIMATION_STYLEABLE; } if (transit == TRANSIT_ENTER || transit == TRANSIT_SHOW) { return R.anim.fade_in; } else if (transit == TRANSIT_EXIT) { return R.anim.fade_out; } else { - return 0; + return ANIMATION_STYLEABLE; } } diff --git a/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java b/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java index 7e085f677be9..bc95481dcee9 100644 --- a/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java +++ b/services/core/java/com/android/server/wm/ImeInsetsSourceProvider.java @@ -25,7 +25,7 @@ import android.view.WindowInsets; */ class ImeInsetsSourceProvider extends InsetsSourceProvider { - private WindowState mCurImeTarget; + private WindowState mImeTargetFromIme; private Runnable mShowImeRunner; private boolean mIsImeLayoutDrawn; @@ -40,8 +40,8 @@ class ImeInsetsSourceProvider extends InsetsSourceProvider { void onPostLayout() { super.onPostLayout(); - if (mCurImeTarget != null - && mCurImeTarget == mDisplayContent.mInputMethodTarget + if (mImeTargetFromIme != null + && isImeTargetFromDisplayContentAndImeSame() && mWin != null && mWin.isDrawnLw() && !mWin.mGivenInsetsPending) { @@ -64,18 +64,33 @@ class ImeInsetsSourceProvider extends InsetsSourceProvider { /** * Called from {@link WindowManagerInternal#showImePostLayout} when {@link InputMethodService} * requests to show IME on {@param imeTarget}. - * @param imeTarget imeTarget on which IME is displayed. + * @param imeTarget imeTarget on which IME request is coming from. */ void scheduleShowImePostLayout(WindowState imeTarget) { - mCurImeTarget = imeTarget; + mImeTargetFromIme = imeTarget; mShowImeRunner = () -> { // Target should still be the same. - if (mCurImeTarget == mDisplayContent.mInputMethodTarget) { + if (isImeTargetFromDisplayContentAndImeSame()) { mDisplayContent.mInputMethodTarget.showInsets( WindowInsets.Type.ime(), true /* fromIme */); } - mCurImeTarget = null; + mImeTargetFromIme = null; }; } + private boolean isImeTargetFromDisplayContentAndImeSame() { + // IMMS#mLastImeTargetWindow always considers focused window as + // IME target, however DisplayContent#computeImeTarget() can compute + // a different IME target. + // Refer to WindowManagerService#applyImeVisibility(token, false). + // If IMMS's imeTarget is child of DisplayContent's imeTarget and child window + // is above the parent, we will consider it as the same target for now. + // TODO(b/139861270): Remove the child & sublayer check once IMMS is aware of + // actual IME target. + return mImeTargetFromIme == mDisplayContent.mInputMethodTarget + || (mDisplayContent.mInputMethodTarget.getParentWindow() == mImeTargetFromIme + && mDisplayContent.mInputMethodTarget.mSubLayer + > mImeTargetFromIme.mSubLayer); + } + } diff --git a/services/core/java/com/android/server/wm/ImmersiveModeConfirmation.java b/services/core/java/com/android/server/wm/ImmersiveModeConfirmation.java index d774dc3fd2f1..82ac6b897a8f 100644 --- a/services/core/java/com/android/server/wm/ImmersiveModeConfirmation.java +++ b/services/core/java/com/android/server/wm/ImmersiveModeConfirmation.java @@ -189,7 +189,7 @@ public class ImmersiveModeConfirmation { | WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL, PixelFormat.TRANSLUCENT); - lp.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS; + lp.privateFlags |= WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS; lp.setTitle("ImmersiveModeConfirmation"); lp.windowAnimations = com.android.internal.R.style.Animation_ImmersiveModeConfirmation; lp.token = getWindowToken(); diff --git a/services/core/java/com/android/server/wm/InputManagerCallback.java b/services/core/java/com/android/server/wm/InputManagerCallback.java index eb9b3e92e21a..9973e11b94bc 100644 --- a/services/core/java/com/android/server/wm/InputManagerCallback.java +++ b/services/core/java/com/android/server/wm/InputManagerCallback.java @@ -266,7 +266,8 @@ final class InputManagerCallback implements InputManagerService.WindowManagerCal final IWindow focusedWindow = mFocusedWindow.get(); if (focusedWindow != null) { - if (focusedWindow.asBinder() == newFocusedWindow.asBinder()) { + if (newFocusedWindow != null + && newFocusedWindow.asBinder() == focusedWindow.asBinder()) { Slog.w(TAG, "notifyFocusChanged called with unchanged mFocusedWindow=" + focusedWindow); return false; diff --git a/services/core/java/com/android/server/wm/InputMonitor.java b/services/core/java/com/android/server/wm/InputMonitor.java index dc9a598e2a81..584c6e19bfa1 100644 --- a/services/core/java/com/android/server/wm/InputMonitor.java +++ b/services/core/java/com/android/server/wm/InputMonitor.java @@ -112,7 +112,7 @@ final class InputMonitor { } } - private final Runnable mUpdateInputWindows = new Runnable() { + private class UpdateInputWindows implements Runnable { @Override public void run() { synchronized (mService.mGlobalLock) { @@ -148,7 +148,9 @@ final class InputMonitor { mUpdateInputForAllWindowsConsumer.updateInputWindows(inDrag); } } - }; + } + + private final UpdateInputWindows mUpdateInputWindows = new UpdateInputWindows(); public InputMonitor(WindowManagerService service, int displayId) { mService = service; diff --git a/services/core/java/com/android/server/wm/InsetsControlTarget.java b/services/core/java/com/android/server/wm/InsetsControlTarget.java index b7184a5e83d5..22ba82ace865 100644 --- a/services/core/java/com/android/server/wm/InsetsControlTarget.java +++ b/services/core/java/com/android/server/wm/InsetsControlTarget.java @@ -33,4 +33,13 @@ interface InsetsControlTarget { */ default void showInsets(@InsetType int types, boolean fromIme) { } + + /** + * Instructs the control target to hide inset sources. + * + * @param types to specify which types of insets source window should be hidden. + * @param fromIme {@code true} if IME hide request originated from {@link InputMethodService}. + */ + default void hideInsets(@InsetType int types, boolean fromIme) { + } } diff --git a/services/core/java/com/android/server/wm/KeyguardController.java b/services/core/java/com/android/server/wm/KeyguardController.java index 2b5eb3ac29fb..52cc422f8c51 100644 --- a/services/core/java/com/android/server/wm/KeyguardController.java +++ b/services/core/java/com/android/server/wm/KeyguardController.java @@ -16,7 +16,7 @@ package com.android.server.wm; -import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER; +import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER; import static android.view.Display.DEFAULT_DISPLAY; import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_NO_ANIMATION; import static android.view.WindowManager.TRANSIT_FLAG_KEYGUARD_GOING_AWAY_SUBTLE_ANIMATION; @@ -180,7 +180,7 @@ class KeyguardController { if (!mKeyguardShowing) { return; } - Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "keyguardGoingAway"); + Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "keyguardGoingAway"); mService.deferWindowLayout(); try { setKeyguardGoingAway(true); @@ -202,11 +202,8 @@ class KeyguardController { true /* taskSwitch */); mWindowManager.executeAppTransition(); } finally { - Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "keyguardGoingAway: surfaceLayout"); mService.continueWindowLayout(); - Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER); - - Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER); + Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); } } diff --git a/services/core/java/com/android/server/wm/LaunchObserverRegistryImpl.java b/services/core/java/com/android/server/wm/LaunchObserverRegistryImpl.java index 93e2d8d6fba4..362ed3c380c5 100644 --- a/services/core/java/com/android/server/wm/LaunchObserverRegistryImpl.java +++ b/services/core/java/com/android/server/wm/LaunchObserverRegistryImpl.java @@ -70,9 +70,12 @@ class LaunchObserverRegistryImpl implements } @Override - public void onIntentStarted(Intent intent) { + public void onIntentStarted(Intent intent, long timestampNs) { mHandler.sendMessage(PooledLambda.obtainMessage( - LaunchObserverRegistryImpl::handleOnIntentStarted, this, intent)); + LaunchObserverRegistryImpl::handleOnIntentStarted, + this, + intent, + timestampNs)); } @Override @@ -99,9 +102,22 @@ class LaunchObserverRegistryImpl implements @Override public void onActivityLaunchFinished( - @ActivityRecordProto byte[] activity) { + @ActivityRecordProto byte[] activity, + long timestampNs) { + mHandler.sendMessage(PooledLambda.obtainMessage( + LaunchObserverRegistryImpl::handleOnActivityLaunchFinished, + this, + activity, + timestampNs)); + } + + @Override + public void onReportFullyDrawn(@ActivityRecordProto byte[] activity, long timestampNs) { mHandler.sendMessage(PooledLambda.obtainMessage( - LaunchObserverRegistryImpl::handleOnActivityLaunchFinished, this, activity)); + LaunchObserverRegistryImpl::handleOnReportFullyDrawn, + this, + activity, + timestampNs)); } // Use PooledLambda.obtainMessage to invoke below methods. Every method reference must be @@ -116,11 +132,11 @@ class LaunchObserverRegistryImpl implements mList.remove(observer); } - private void handleOnIntentStarted(Intent intent) { + private void handleOnIntentStarted(Intent intent, long timestampNs) { // Traverse start-to-end to meet the registerLaunchObserver multi-cast order guarantee. for (int i = 0; i < mList.size(); i++) { ActivityMetricsLaunchObserver o = mList.get(i); - o.onIntentStarted(intent); + o.onIntentStarted(intent, timestampNs); } } @@ -152,11 +168,20 @@ class LaunchObserverRegistryImpl implements } private void handleOnActivityLaunchFinished( - @ActivityRecordProto byte[] activity) { + @ActivityRecordProto byte[] activity, long timestampNs) { + // Traverse start-to-end to meet the registerLaunchObserver multi-cast order guarantee. + for (int i = 0; i < mList.size(); i++) { + ActivityMetricsLaunchObserver o = mList.get(i); + o.onActivityLaunchFinished(activity, timestampNs); + } + } + + private void handleOnReportFullyDrawn( + @ActivityRecordProto byte[] activity, long timestampNs) { // Traverse start-to-end to meet the registerLaunchObserver multi-cast order guarantee. for (int i = 0; i < mList.size(); i++) { ActivityMetricsLaunchObserver o = mList.get(i); - o.onActivityLaunchFinished(activity); + o.onReportFullyDrawn(activity, timestampNs); } } } diff --git a/services/core/java/com/android/server/wm/RecentsAnimation.java b/services/core/java/com/android/server/wm/RecentsAnimation.java index 062cdc5b2b60..12579e6b4058 100644 --- a/services/core/java/com/android/server/wm/RecentsAnimation.java +++ b/services/core/java/com/android/server/wm/RecentsAnimation.java @@ -22,7 +22,7 @@ import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS; import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK; import static android.content.Intent.FLAG_ACTIVITY_NO_ANIMATION; -import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER; +import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER; import static android.view.WindowManager.TRANSIT_NONE; import static com.android.server.wm.ActivityStackSupervisor.PRESERVE_WINDOWS; @@ -158,7 +158,7 @@ class RecentsAnimation implements RecentsAnimationCallbacks, void startRecentsActivity(IRecentsAnimationRunner recentsAnimationRunner) { ProtoLog.d(WM_DEBUG_RECENTS_ANIMATIONS, "startRecentsActivity(): intent=%s", mTargetIntent); - Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "RecentsAnimation#startRecentsActivity"); + Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "RecentsAnimation#startRecentsActivity"); // TODO(multi-display) currently only support recents animation in default display. final DisplayContent dc = @@ -263,7 +263,7 @@ class RecentsAnimation implements RecentsAnimationCallbacks, throw e; } finally { mService.continueWindowLayout(); - Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER); + Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); } } @@ -297,7 +297,7 @@ class RecentsAnimation implements RecentsAnimationCallbacks, } mWindowManager.inSurfaceTransaction(() -> { - Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, + Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "RecentsAnimation#onAnimationFinished_inSurfaceTransaction"); mService.deferWindowLayout(); try { @@ -394,7 +394,7 @@ class RecentsAnimation implements RecentsAnimationCallbacks, throw e; } finally { mService.continueWindowLayout(); - Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER); + Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); } }); } diff --git a/services/core/java/com/android/server/wm/RecentsAnimationController.java b/services/core/java/com/android/server/wm/RecentsAnimationController.java index bd27905e1a0f..d606e5d8c1a2 100644 --- a/services/core/java/com/android/server/wm/RecentsAnimationController.java +++ b/services/core/java/com/android/server/wm/RecentsAnimationController.java @@ -98,8 +98,10 @@ public class RecentsAnimationController implements DeathRecipient { private final ArrayList<WallpaperAnimationAdapter> mPendingWallpaperAnimations = new ArrayList<>(); private final int mDisplayId; - private final Runnable mFailsafeRunnable = () -> - cancelAnimation(REORDER_MOVE_TO_ORIGINAL_POSITION, "failSafeRunnable"); + private boolean mWillFinishToHome = false; + private final Runnable mFailsafeRunnable = () -> cancelAnimation( + mWillFinishToHome ? REORDER_MOVE_TO_TOP : REORDER_MOVE_TO_ORIGINAL_POSITION, + "failSafeRunnable"); // The recents component app token that is shown behind the visibile tasks private AppWindowToken mTargetAppToken; @@ -326,6 +328,13 @@ public class RecentsAnimationController implements DeathRecipient { } } } + + @Override + public void setWillFinishToHome(boolean willFinishToHome) { + synchronized (mService.getWindowManagerLock()) { + mWillFinishToHome = willFinishToHome; + } + } }; /** @@ -494,7 +503,8 @@ public class RecentsAnimationController implements DeathRecipient { } final SparseIntArray reasons = new SparseIntArray(); reasons.put(WINDOWING_MODE_FULLSCREEN, APP_TRANSITION_RECENTS_ANIM); - mService.mAtmInternal.notifyAppTransitionStarting(reasons, SystemClock.uptimeMillis()); + mService.mAtmInternal + .notifyAppTransitionStarting(reasons, SystemClock.elapsedRealtimeNanos()); } private RemoteAnimationTarget[] createAppAnimations() { diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index c5e9edb7727e..f0717ca34c4e 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -121,8 +121,11 @@ class Task extends WindowContainer<ActivityRecord> implements ConfigurationConta // TODO: Remove after unification. @Override public void onConfigurationChanged(Configuration newParentConfig) { - // Only forward configuration changes in cases where children won't get it from TaskRecord. - onConfigurationChanged(newParentConfig, mTaskRecord == null /*forwardToChildren*/); + // Forward configuration changes in cases + // - children won't get it from TaskRecord + // - it's a pinned task + onConfigurationChanged(newParentConfig, + (mTaskRecord == null) || inPinnedWindowingMode() /*forwardToChildren*/); } Task(int taskId, TaskStack stack, int userId, WindowManagerService service, int resizeMode, diff --git a/services/core/java/com/android/server/wm/TaskRecord.java b/services/core/java/com/android/server/wm/TaskRecord.java index ab661cad865f..299b32cce039 100644 --- a/services/core/java/com/android/server/wm/TaskRecord.java +++ b/services/core/java/com/android/server/wm/TaskRecord.java @@ -48,7 +48,7 @@ import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE_VIA_SDK_VER import static android.content.res.Configuration.ORIENTATION_LANDSCAPE; import static android.content.res.Configuration.ORIENTATION_PORTRAIT; import static android.content.res.Configuration.ORIENTATION_UNDEFINED; -import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER; +import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER; import static android.provider.Settings.Secure.USER_SETUP_COMPLETE; import static android.view.Display.DEFAULT_DISPLAY; @@ -553,7 +553,7 @@ class TaskRecord extends ConfigurationContainer { // This method assumes that the task is already placed in the right stack. // we do not mess with that decision and we only do the resize! - Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "am.resizeTask_" + mTaskId); + Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "resizeTask_" + mTaskId); boolean updatedConfig = false; mTmpConfig.setTo(getResolvedOverrideConfiguration()); @@ -587,7 +587,7 @@ class TaskRecord extends ConfigurationContainer { saveLaunchingStateIfNeeded(); - Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER); + Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); return kept; } finally { mAtmService.continueWindowLayout(); diff --git a/services/core/java/com/android/server/wm/WindowManagerInternal.java b/services/core/java/com/android/server/wm/WindowManagerInternal.java index f4b76729b7ea..0cb4826fdfc3 100644 --- a/services/core/java/com/android/server/wm/WindowManagerInternal.java +++ b/services/core/java/com/android/server/wm/WindowManagerInternal.java @@ -514,6 +514,13 @@ public abstract class WindowManagerInternal { public abstract void showImePostLayout(IBinder imeTargetWindowToken); /** + * Hide IME using imeTargetWindow when requested. + * + * @param displayId on which IME is shown + */ + public abstract void hideIme(int displayId); + + /** * Tell window manager about a package that should not be running with high refresh rate * setting until removeNonHighRefreshRatePackage is called for the same package. * diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 0287270c7014..c48528042348 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -236,6 +236,7 @@ import android.view.SurfaceControl; import android.view.SurfaceSession; import android.view.View; import android.view.WindowContentFrameStats; +import android.view.WindowInsets; import android.view.WindowManager; import android.view.WindowManager.LayoutParams; import android.view.WindowManager.RemoveContentMode; @@ -7312,6 +7313,16 @@ public class WindowManagerService extends IWindowManager.Stub } @Override + public void hideIme(int displayId) { + synchronized (mGlobalLock) { + final DisplayContent dc = mRoot.getDisplayContent(displayId); + if (dc != null && dc.mInputMethodTarget != null) { + dc.mInputMethodTarget.hideInsets(WindowInsets.Type.ime(), true /* fromIme */); + } + } + } + + @Override public boolean isUidAllowedOnDisplay(int displayId, int uid) { if (displayId == Display.DEFAULT_DISPLAY) { return true; diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index 40bb0597bfd1..7ff9b7057653 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -3377,6 +3377,15 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP } } + @Override + public void hideInsets(@InsetType int types, boolean fromIme) { + try { + mClient.hideInsets(types, fromIme); + } catch (RemoteException e) { + Slog.w(TAG, "Failed to deliver showInsets", e); + } + } + Rect getBackdropFrame(Rect frame) { // When the task is docked, we send fullscreen sized backDropFrame as soon as resizing // start even if we haven't received the relayout window, so that the client requests diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java index 1328273c9560..eac372f5da81 100644 --- a/services/core/java/com/android/server/wm/WindowStateAnimator.java +++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java @@ -1348,13 +1348,16 @@ class WindowStateAnimator { // frozen, there is no reason to animate and it can cause strange // artifacts when we unfreeze the display if some different animation // is running. - Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "WSA#applyAnimationLocked"); if (mWin.mToken.okToAnimate()) { - int anim = mWin.getDisplayContent().getDisplayPolicy().selectAnimationLw(mWin, transit); + int anim = mWin.getDisplayContent().getDisplayPolicy().selectAnimation(mWin, transit); int attr = -1; Animation a = null; - if (anim != 0) { - a = anim != -1 ? AnimationUtils.loadAnimation(mContext, anim) : null; + if (anim != DisplayPolicy.ANIMATION_STYLEABLE) { + if (anim != DisplayPolicy.ANIMATION_NONE) { + Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "WSA#loadAnimation"); + a = AnimationUtils.loadAnimation(mContext, anim); + Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER); + } } else { switch (transit) { case WindowManagerPolicy.TRANSIT_ENTER: @@ -1384,7 +1387,9 @@ class WindowStateAnimator { + " isEntrance=" + isEntrance + " Callers " + Debug.getCallers(3)); if (a != null) { if (DEBUG_ANIM) logWithStack(TAG, "Loaded animation " + a + " for " + this); + Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "WSA#startAnimation"); mWin.startAnimation(a); + Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER); mAnimationIsEntrance = isEntrance; } } else { @@ -1395,7 +1400,6 @@ class WindowStateAnimator { mWin.getDisplayContent().adjustForImeIfNeeded(); } - Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER); return mWin.isAnimating(); } diff --git a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java index 56f6d4b02e32..0cfdebc6792d 100644 --- a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java +++ b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java @@ -23,7 +23,6 @@ import static com.android.server.wm.WindowManagerService.H.REPORT_WINDOWS_CHANGE import static com.android.server.wm.WindowManagerService.LAYOUT_REPEAT_THRESHOLD; import android.os.Debug; -import android.os.Trace; import android.util.Slog; import java.io.PrintWriter; @@ -52,15 +51,19 @@ class WindowSurfacePlacer { /** The number of layout requests when deferring. */ private int mDeferredRequests; - private final Runnable mPerformSurfacePlacement; - - public WindowSurfacePlacer(WindowManagerService service) { - mService = service; - mPerformSurfacePlacement = () -> { + private class Traverser implements Runnable { + @Override + public void run() { synchronized (mService.mGlobalLock) { performSurfacePlacement(); } - }; + } + } + + private final Traverser mPerformSurfacePlacement = new Traverser(); + + WindowSurfacePlacer(WindowManagerService service) { + mService = service; } /** @@ -152,7 +155,6 @@ class WindowSurfacePlacer { return; } - Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "wmLayout"); mInLayout = true; boolean recoveringMemory = false; @@ -198,8 +200,6 @@ class WindowSurfacePlacer { mInLayout = false; Slog.wtf(TAG, "Unhandled exception while laying out windows", e); } - - Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER); } void debugLayoutRepeats(final String msg, int pendingLayoutChanges) { diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp index fb2fdab19a54..dd2629d31768 100644 --- a/services/core/jni/com_android_server_input_InputManagerService.cpp +++ b/services/core/jni/com_android_server_input_InputManagerService.cpp @@ -201,8 +201,7 @@ public: void setDisplayViewports(JNIEnv* env, jobjectArray viewportObjArray); - status_t registerInputChannel(JNIEnv* env, const sp<InputChannel>& inputChannel, - int32_t displayId); + status_t registerInputChannel(JNIEnv* env, const sp<InputChannel>& inputChannel); status_t registerInputMonitor(JNIEnv* env, const sp<InputChannel>& inputChannel, int32_t displayId, bool isGestureMonitor); status_t unregisterInputChannel(JNIEnv* env, const sp<InputChannel>& inputChannel); @@ -435,10 +434,9 @@ void NativeInputManager::setDisplayViewports(JNIEnv* env, jobjectArray viewportO } status_t NativeInputManager::registerInputChannel(JNIEnv* /* env */, - const sp<InputChannel>& inputChannel, int32_t displayId) { + const sp<InputChannel>& inputChannel) { ATRACE_CALL(); - return mInputManager->getDispatcher()->registerInputChannel( - inputChannel, displayId); + return mInputManager->getDispatcher()->registerInputChannel(inputChannel); } status_t NativeInputManager::registerInputMonitor(JNIEnv* /* env */, @@ -1405,7 +1403,7 @@ static void handleInputChannelDisposed(JNIEnv* env, } static void nativeRegisterInputChannel(JNIEnv* env, jclass /* clazz */, - jlong ptr, jobject inputChannelObj, jint displayId) { + jlong ptr, jobject inputChannelObj) { NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr); sp<InputChannel> inputChannel = android_view_InputChannel_getInputChannel(env, @@ -1415,7 +1413,7 @@ static void nativeRegisterInputChannel(JNIEnv* env, jclass /* clazz */, return; } - status_t status = im->registerInputChannel(env, inputChannel, displayId); + status_t status = im->registerInputChannel(env, inputChannel); if (status) { std::string message; @@ -1757,7 +1755,7 @@ static const JNINativeMethod gInputManagerMethods[] = { { "nativeHasKeys", "(JII[I[Z)Z", (void*) nativeHasKeys }, { "nativeRegisterInputChannel", - "(JLandroid/view/InputChannel;I)V", + "(JLandroid/view/InputChannel;)V", (void*) nativeRegisterInputChannel }, { "nativeRegisterInputMonitor", "(JLandroid/view/InputChannel;IZ)V", diff --git a/services/devicepolicy/Android.bp b/services/devicepolicy/Android.bp index 47790ce68dc1..91c05a858ce4 100644 --- a/services/devicepolicy/Android.bp +++ b/services/devicepolicy/Android.bp @@ -5,4 +5,13 @@ java_library_static { libs: [ "services.core", ], + + plugins: [ + "compat-changeid-annotation-processor", + ], } + +platform_compat_config { + name: "services-devicepolicy-platform-compat-config", + src: ":services.devicepolicy", +}
\ No newline at end of file diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index 9569ac8dfdd1..0ae205a16a7b 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -137,6 +137,8 @@ import android.app.admin.SystemUpdatePolicy; import android.app.backup.IBackupManager; import android.app.trust.TrustManager; import android.app.usage.UsageStatsManagerInternal; +import android.compat.annotation.ChangeId; +import android.compat.annotation.EnabledAfter; import android.content.ActivityNotFoundException; import android.content.BroadcastReceiver; import android.content.ComponentName; @@ -235,6 +237,7 @@ import android.view.inputmethod.InputMethodInfo; import com.android.internal.R; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.compat.IPlatformCompat; import com.android.internal.logging.MetricsLogger; import com.android.internal.messages.nano.SystemMessageProto.SystemMessage; import com.android.internal.notification.SystemNotificationChannels; @@ -251,6 +254,7 @@ import com.android.internal.util.StatLogger; import com.android.internal.util.XmlUtils; import com.android.internal.widget.LockPatternUtils; import com.android.internal.widget.LockSettingsInternal; +import com.android.internal.widget.LockscreenCredential; import com.android.server.LocalServices; import com.android.server.LockGuard; import com.android.server.SystemServerInitThreadPool; @@ -494,6 +498,22 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { private static final String LOG_TAG_PROFILE_OWNER = "profile-owner"; private static final String LOG_TAG_DEVICE_OWNER = "device-owner"; + /** + * For admin apps targeting R+, throw when the app sets password requirement + * that is not taken into account at given quality. For example when quality is set + * to {@link DevicePolicyManager#PASSWORD_QUALITY_UNSPECIFIED}, it doesn't make sense to + * require certain password length. If the intent is to require a password of certain length + * having at least NUMERIC quality, the admin should first call + * {@link #setPasswordQuality(ComponentName, int, boolean)} and only then call + * {@link #setPasswordMinimumLength(ComponentName, int, boolean)}. + * + * <p>Conversely when an admin app targeting R+ lowers password quality, those + * requirements that stop making sense are reset to default values. + */ + @ChangeId + @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.Q) + private static final long ADMIN_APP_PASSWORD_COMPLEXITY = 123562444L; + final Context mContext; final Injector mInjector; final IPackageManager mIPackageManager; @@ -506,6 +526,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { private final LockSettingsInternal mLockSettingsInternal; private final DeviceAdminServiceController mDeviceAdminServiceController; private final OverlayPackagesProvider mOverlayPackagesProvider; + private final IPlatformCompat mIPlatformCompat; private final DevicePolicyCacheImpl mPolicyCache = new DevicePolicyCacheImpl(); private final DeviceStateCacheImpl mStateCache = new DeviceStateCacheImpl(); @@ -1984,6 +2005,11 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { return LocalServices.getService(LockSettingsInternal.class); } + IPlatformCompat getIPlatformCompat() { + return IPlatformCompat.Stub.asInterface( + ServiceManager.getService(Context.PLATFORM_COMPAT_SERVICE)); + } + boolean hasUserSetupCompleted(DevicePolicyData userData) { return userData.mUserSetupComplete; } @@ -2222,6 +2248,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { mUsageStatsManagerInternal = Preconditions.checkNotNull( injector.getUsageStatsManagerInternal()); mIPackageManager = Preconditions.checkNotNull(injector.getIPackageManager()); + mIPlatformCompat = Preconditions.checkNotNull(injector.getIPlatformCompat()); mIPermissionManager = Preconditions.checkNotNull(injector.getIPermissionManager()); mTelephonyManager = Preconditions.checkNotNull(injector.getTelephonyManager()); @@ -4156,12 +4183,22 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { .write(); } + private boolean passwordQualityInvocationOrderCheckEnabled(String packageName, int userId) { + try { + return mIPlatformCompat.isChangeEnabledByPackageName(ADMIN_APP_PASSWORD_COMPLEXITY, + packageName); + } catch (RemoteException e) { + Log.e(LOG_TAG, "Failed to get a response from PLATFORM_COMPAT_SERVICE", e); + } + return getTargetSdk(packageName, userId) > Build.VERSION_CODES.Q; + } + /** * For admins targeting R+ reset various password constraints to default values when quality is * set to a value that makes those constraints that have no effect. */ private void resetInactivePasswordRequirementsIfRPlus(int userId, ActiveAdmin admin) { - if (getTargetSdk(admin.info.getPackageName(), userId) > Build.VERSION_CODES.Q) { + if (passwordQualityInvocationOrderCheckEnabled(admin.info.getPackageName(), userId)) { final PasswordMetrics metrics = admin.minimumPasswordMetrics; if (metrics.quality < PASSWORD_QUALITY_NUMERIC) { metrics.length = ActiveAdmin.DEF_MINIMUM_PASSWORD_LENGTH; @@ -4326,7 +4363,8 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { private void ensureMinimumQuality( int userId, ActiveAdmin admin, int minimumQuality, String operation) { if (admin.minimumPasswordMetrics.quality < minimumQuality - && getTargetSdk(admin.info.getPackageName(), userId) > Build.VERSION_CODES.Q) { + && passwordQualityInvocationOrderCheckEnabled(admin.info.getPackageName(), + userId)) { throw new IllegalStateException(String.format( "password quality should be at least %d for %s", minimumQuality, operation)); } @@ -5222,28 +5260,20 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager { // back in to the service. final long ident = mInjector.binderClearCallingIdentity(); final boolean result; + final LockscreenCredential newCredential = + LockscreenCredential.createPasswordOrNone(password); try { if (token == null) { // This is the legacy reset password for DPM. Here we want to be able to override // the old device password without necessarily knowing it. - if (!TextUtils.isEmpty(password)) { - mLockPatternUtils.saveLockPassword(password.getBytes(), null, quality, - userHandle, /*allowUntrustedChange */true); - } else { - mLockPatternUtils.clearLock(null, userHandle, - /*allowUntrustedChange */ true); - } + mLockPatternUtils.setLockCredential( + newCredential, + LockscreenCredential.createNone(), + userHandle, /*allowUntrustedChange */true); result = true; } else { - if (!TextUtils.isEmpty(password)) { - result = mLockPatternUtils.setLockCredentialWithToken(password.getBytes(), - LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, - quality, tokenHandle, token, userHandle); - } else { - result = mLockPatternUtils.setLockCredentialWithToken(null, - LockPatternUtils.CREDENTIAL_TYPE_NONE, - quality, tokenHandle, token, userHandle); - } + result = mLockPatternUtils.setLockCredentialWithToken(newCredential, tokenHandle, + token, userHandle); } boolean requireEntry = (flags & DevicePolicyManager.RESET_PASSWORD_REQUIRE_ENTRY) != 0; if (requireEntry) { diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/RemoteBugreportUtils.java b/services/devicepolicy/java/com/android/server/devicepolicy/RemoteBugreportUtils.java index 0838fbc536c1..7cfbcc87edc3 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/RemoteBugreportUtils.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/RemoteBugreportUtils.java @@ -51,7 +51,7 @@ class RemoteBugreportUtils { static final long REMOTE_BUGREPORT_TIMEOUT_MILLIS = 10 * DateUtils.MINUTE_IN_MILLIS; static final String CTL_STOP = "ctl.stop"; - static final String REMOTE_BUGREPORT_SERVICE = "bugreportremote"; + static final String REMOTE_BUGREPORT_SERVICE = "bugreportd"; static final String BUGREPORT_MIMETYPE = "application/vnd.android.bugreport"; diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index 4cf98d32f8de..88859a71d135 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -921,7 +921,6 @@ public final class SystemServer { false); boolean disableCameraService = SystemProperties.getBoolean("config.disable_cameraservice", false); - boolean disableSlices = SystemProperties.getBoolean("config.disable_slices", false); boolean enableLeftyService = SystemProperties.getBoolean("config.enable_lefty", false); boolean isEmulator = SystemProperties.get("ro.kernel.qemu").equals("1"); @@ -1856,7 +1855,7 @@ public final class SystemServer { t.traceEnd(); } - if (!disableSlices) { + if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_SLICES_DISABLED)) { t.traceBegin("StartSliceManagerService"); mSystemServiceManager.startService(SLICE_MANAGER_SERVICE_CLASS); t.traceEnd(); diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java index edf82ee30e2d..597d337c2450 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityServiceConnectionTest.java @@ -41,12 +41,14 @@ import android.content.pm.ServiceInfo; import android.os.IBinder; import android.os.RemoteException; import android.os.UserHandle; +import android.testing.DexmakerShareClassLoaderRule; import android.view.Display; import com.android.server.wm.WindowManagerInternal; import org.junit.After; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; import org.mockito.Mock; import org.mockito.MockitoAnnotations; @@ -65,9 +67,14 @@ public class AccessibilityServiceConnectionTest { "com.android.server.accessibility", "AccessibilityServiceConnectionTest"); static final int SERVICE_ID = 42; + // Mock package-private AccessibilityUserState class + @Rule + public final DexmakerShareClassLoaderRule mDexmakerShareClassLoaderRule = + new DexmakerShareClassLoaderRule(); + AccessibilityServiceConnection mConnection; - @Mock AccessibilityManagerService.UserState mMockUserState; + @Mock AccessibilityUserState mMockUserState; @Mock Context mMockContext; @Mock AccessibilityServiceInfo mMockServiceInfo; @Mock ResolveInfo mMockResolveInfo; diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityUserStateTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityUserStateTest.java new file mode 100644 index 000000000000..918005437e96 --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityUserStateTest.java @@ -0,0 +1,297 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.accessibility; + +import static android.accessibilityservice.AccessibilityService.SHOW_MODE_AUTO; +import static android.accessibilityservice.AccessibilityService.SHOW_MODE_HARD_KEYBOARD_ORIGINAL_VALUE; +import static android.accessibilityservice.AccessibilityService.SHOW_MODE_HARD_KEYBOARD_OVERRIDDEN; +import static android.accessibilityservice.AccessibilityService.SHOW_MODE_HIDDEN; +import static android.accessibilityservice.AccessibilityService.SHOW_MODE_IGNORE_HARD_KEYBOARD; +import static android.view.accessibility.AccessibilityManager.STATE_FLAG_ACCESSIBILITY_ENABLED; +import static android.view.accessibility.AccessibilityManager.STATE_FLAG_HIGH_TEXT_CONTRAST_ENABLED; +import static android.view.accessibility.AccessibilityManager.STATE_FLAG_TOUCH_EXPLORATION_ENABLED; + +import static junit.framework.Assert.assertEquals; +import static junit.framework.Assert.assertFalse; +import static junit.framework.Assert.assertNull; +import static junit.framework.Assert.assertTrue; + +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.accessibilityservice.AccessibilityServiceInfo; +import android.content.ComponentName; +import android.content.Context; +import android.provider.Settings; +import android.test.mock.MockContentResolver; +import android.testing.DexmakerShareClassLoaderRule; + +import com.android.internal.util.test.FakeSettingsProvider; + +import org.junit.After; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +/** Tests for AccessibilityUserState */ +public class AccessibilityUserStateTest { + + private static final ComponentName COMPONENT_NAME = + new ComponentName("com.android.server.accessibility", "AccessibilityUserStateTest"); + + // Values of setting key SHOW_IME_WITH_HARD_KEYBOARD + private static final int STATE_HIDE_IME = 0; + private static final int STATE_SHOW_IME = 1; + + private static final int USER_ID = 42; + + // Mock package-private class AccessibilityServiceConnection + @Rule public final DexmakerShareClassLoaderRule mDexmakerShareClassLoaderRule = + new DexmakerShareClassLoaderRule(); + + @Mock private AccessibilityServiceInfo mMockServiceInfo; + + @Mock private AccessibilityServiceConnection mMockConnection; + + @Mock private AccessibilityUserState.ServiceInfoChangeListener mMockListener; + + @Mock private Context mContext; + + private MockContentResolver mMockResolver; + + private AccessibilityUserState mUserState; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + FakeSettingsProvider.clearSettingsProvider(); + mMockResolver = new MockContentResolver(); + mMockResolver.addProvider(Settings.AUTHORITY, new FakeSettingsProvider()); + when(mContext.getContentResolver()).thenReturn(mMockResolver); + when(mMockServiceInfo.getComponentName()).thenReturn(COMPONENT_NAME); + when(mMockConnection.getServiceInfo()).thenReturn(mMockServiceInfo); + + mUserState = new AccessibilityUserState(USER_ID, mContext, mMockListener); + } + + @After + public void tearDown() { + FakeSettingsProvider.clearSettingsProvider(); + } + + @Test + public void onSwitchToAnotherUser_userStateClearedNonDefaultValues() { + mUserState.getBoundServicesLocked().add(mMockConnection); + mUserState.getBindingServicesLocked().add(COMPONENT_NAME); + mUserState.setLastSentClientStateLocked( + STATE_FLAG_ACCESSIBILITY_ENABLED + | STATE_FLAG_TOUCH_EXPLORATION_ENABLED + | STATE_FLAG_HIGH_TEXT_CONTRAST_ENABLED); + mUserState.setNonInteractiveUiTimeoutLocked(30); + mUserState.setInteractiveUiTimeoutLocked(30); + mUserState.mEnabledServices.add(COMPONENT_NAME); + mUserState.mTouchExplorationGrantedServices.add(COMPONENT_NAME); + mUserState.setTouchExplorationEnabledLocked(true); + mUserState.setDisplayMagnificationEnabledLocked(true); + mUserState.setNavBarMagnificationEnabledLocked(true); + mUserState.setServiceAssignedToAccessibilityButtonLocked(COMPONENT_NAME); + mUserState.setNavBarMagnificationAssignedToAccessibilityButtonLocked(true); + mUserState.setAutoclickEnabledLocked(true); + mUserState.setUserNonInteractiveUiTimeoutLocked(30); + mUserState.setUserInteractiveUiTimeoutLocked(30); + + mUserState.onSwitchToAnotherUserLocked(); + + verify(mMockConnection).unbindLocked(); + assertTrue(mUserState.getBoundServicesLocked().isEmpty()); + assertTrue(mUserState.getBindingServicesLocked().isEmpty()); + assertEquals(-1, mUserState.getLastSentClientStateLocked()); + assertEquals(0, mUserState.getNonInteractiveUiTimeoutLocked()); + assertEquals(0, mUserState.getInteractiveUiTimeoutLocked()); + assertTrue(mUserState.mEnabledServices.isEmpty()); + assertTrue(mUserState.mTouchExplorationGrantedServices.isEmpty()); + assertFalse(mUserState.isTouchExplorationEnabledLocked()); + assertFalse(mUserState.isDisplayMagnificationEnabledLocked()); + assertFalse(mUserState.isNavBarMagnificationEnabledLocked()); + assertNull(mUserState.getServiceAssignedToAccessibilityButtonLocked()); + assertFalse(mUserState.isNavBarMagnificationAssignedToAccessibilityButtonLocked()); + assertFalse(mUserState.isAutoclickEnabledLocked()); + assertEquals(0, mUserState.getUserNonInteractiveUiTimeoutLocked()); + assertEquals(0, mUserState.getUserInteractiveUiTimeoutLocked()); + } + + @Test + public void addService_connectionAlreadyAdded_notAddAgain() { + mUserState.getBoundServicesLocked().add(mMockConnection); + + mUserState.addServiceLocked(mMockConnection); + + verify(mMockConnection, never()).onAdded(); + } + + @Test + public void addService_connectionNotYetAddedToBoundService_addAndNotifyServices() { + when(mMockConnection.getComponentName()).thenReturn(COMPONENT_NAME); + + mUserState.addServiceLocked(mMockConnection); + + verify(mMockConnection).onAdded(); + assertTrue(mUserState.getBoundServicesLocked().contains(mMockConnection)); + assertEquals(mMockConnection, mUserState.mComponentNameToServiceMap.get(COMPONENT_NAME)); + verify(mMockListener).onServiceInfoChangedLocked(eq(mUserState)); + } + + @Test + public void reconcileSoftKeyboardMode_whenStateNotMatchSettings_setBothDefault() { + // When soft kb show mode is hidden in settings and is auto in state. + putSecureIntForUser(Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE, + SHOW_MODE_HIDDEN, USER_ID); + + mUserState.reconcileSoftKeyboardModeWithSettingsLocked(); + + assertEquals(SHOW_MODE_AUTO, mUserState.getSoftKeyboardShowModeLocked()); + assertEquals(SHOW_MODE_AUTO, getSecureIntForUser( + Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE, USER_ID)); + assertNull(mUserState.getServiceChangingSoftKeyboardModeLocked()); + } + + @Test + public void + reconcileSoftKeyboardMode_stateIgnoreHardKb_settingsShowImeHardKb_setAutoOverride() { + // When show mode is ignore hard kb without original hard kb value + // and show ime with hard kb is hide + putSecureIntForUser(Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE, + SHOW_MODE_IGNORE_HARD_KEYBOARD, USER_ID); + mUserState.setSoftKeyboardModeLocked(SHOW_MODE_IGNORE_HARD_KEYBOARD, COMPONENT_NAME); + putSecureIntForUser(Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD, + STATE_HIDE_IME, USER_ID); + + mUserState.reconcileSoftKeyboardModeWithSettingsLocked(); + + assertEquals(SHOW_MODE_AUTO | SHOW_MODE_HARD_KEYBOARD_OVERRIDDEN, + getSecureIntForUser(Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE, USER_ID)); + assertNull(mUserState.getServiceChangingSoftKeyboardModeLocked()); + } + + @Test + public void removeService_serviceChangingSoftKeyboardMode_removeAndSetSoftKbModeAuto() { + mUserState.setServiceChangingSoftKeyboardModeLocked(COMPONENT_NAME); + mUserState.mComponentNameToServiceMap.put(COMPONENT_NAME, mMockConnection); + mUserState.setSoftKeyboardModeLocked(SHOW_MODE_HIDDEN, COMPONENT_NAME); + + mUserState.removeServiceLocked(mMockConnection); + + assertFalse(mUserState.getBoundServicesLocked().contains(mMockConnection)); + verify(mMockConnection).onRemoved(); + assertEquals(SHOW_MODE_AUTO, mUserState.getSoftKeyboardShowModeLocked()); + assertNull(mUserState.mComponentNameToServiceMap.get(COMPONENT_NAME)); + verify(mMockListener).onServiceInfoChangedLocked(eq(mUserState)); + } + + @Test + public void serviceDisconnected_removeServiceAndAddToBinding() { + when(mMockConnection.getComponentName()).thenReturn(COMPONENT_NAME); + mUserState.addServiceLocked(mMockConnection); + + mUserState.serviceDisconnectedLocked(mMockConnection); + + assertFalse(mUserState.getBoundServicesLocked().contains(mMockConnection)); + assertTrue(mUserState.getBindingServicesLocked().contains(COMPONENT_NAME)); + } + + @Test + public void setSoftKeyboardMode_withInvalidShowMode_shouldKeepDefaultAuto() { + final int invalidShowMode = SHOW_MODE_HIDDEN | SHOW_MODE_HARD_KEYBOARD_ORIGINAL_VALUE; + + assertFalse(mUserState.setSoftKeyboardModeLocked(invalidShowMode, null)); + + assertEquals(SHOW_MODE_AUTO, mUserState.getSoftKeyboardShowModeLocked()); + } + + @Test + public void setSoftKeyboardMode_newModeSameWithCurrentState_returnTrue() { + when(mMockConnection.getComponentName()).thenReturn(COMPONENT_NAME); + mUserState.addServiceLocked(mMockConnection); + + assertTrue(mUserState.setSoftKeyboardModeLocked(SHOW_MODE_AUTO, null)); + } + + @Test + public void setSoftKeyboardMode_withIgnoreHardKb_whenHardKbOverridden_returnFalseAdNoChange() { + putSecureIntForUser(Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE, + SHOW_MODE_AUTO | SHOW_MODE_HARD_KEYBOARD_OVERRIDDEN, USER_ID); + + assertFalse(mUserState.setSoftKeyboardModeLocked(SHOW_MODE_IGNORE_HARD_KEYBOARD, null)); + + assertEquals(SHOW_MODE_AUTO, mUserState.getSoftKeyboardShowModeLocked()); + } + + @Test + public void + setSoftKeyboardMode_withIgnoreHardKb_whenShowImeWithHardKb_setOriginalHardKbValue() { + putSecureIntForUser(Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD, STATE_SHOW_IME, USER_ID); + + assertTrue(mUserState.setSoftKeyboardModeLocked(SHOW_MODE_IGNORE_HARD_KEYBOARD, null)); + + assertEquals(SHOW_MODE_IGNORE_HARD_KEYBOARD | SHOW_MODE_HARD_KEYBOARD_ORIGINAL_VALUE, + getSecureIntForUser(Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE, USER_ID)); + } + + @Test + public void setSoftKeyboardMode_whenCurrentIgnoreHardKb_shouldSetShowImeWithHardKbValue() { + mUserState.setSoftKeyboardModeLocked(SHOW_MODE_IGNORE_HARD_KEYBOARD, COMPONENT_NAME); + putSecureIntForUser(Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD, STATE_HIDE_IME, USER_ID); + putSecureIntForUser(Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE, + SHOW_MODE_IGNORE_HARD_KEYBOARD | SHOW_MODE_HARD_KEYBOARD_ORIGINAL_VALUE, USER_ID); + + assertTrue(mUserState.setSoftKeyboardModeLocked(SHOW_MODE_AUTO, null)); + + assertEquals(STATE_SHOW_IME, getSecureIntForUser( + Settings.Secure.SHOW_IME_WITH_HARD_KEYBOARD, USER_ID)); + } + + @Test + public void setSoftKeyboardMode_withRequester_shouldUpdateInternalStateAndSettingsAsIs() { + assertTrue(mUserState.setSoftKeyboardModeLocked(SHOW_MODE_HIDDEN, COMPONENT_NAME)); + + assertEquals(SHOW_MODE_HIDDEN, mUserState.getSoftKeyboardShowModeLocked()); + assertEquals(SHOW_MODE_HIDDEN, getSecureIntForUser( + Settings.Secure.ACCESSIBILITY_SOFT_KEYBOARD_MODE, USER_ID)); + assertEquals(COMPONENT_NAME, mUserState.getServiceChangingSoftKeyboardModeLocked()); + } + + @Test + public void setSoftKeyboardMode_shouldNotifyBoundService() { + mUserState.addServiceLocked(mMockConnection); + + assertTrue(mUserState.setSoftKeyboardModeLocked(SHOW_MODE_HIDDEN, COMPONENT_NAME)); + + verify(mMockConnection).notifySoftKeyboardShowModeChangedLocked(eq(SHOW_MODE_HIDDEN)); + } + + private int getSecureIntForUser(String key, int userId) { + return Settings.Secure.getIntForUser(mMockResolver, key, -1, userId); + } + + private void putSecureIntForUser(String key, int value, int userId) { + Settings.Secure.putIntForUser(mMockResolver, key, value, userId); + } +} diff --git a/services/tests/servicestests/src/com/android/server/accessibility/UiAutomationManagerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/UiAutomationManagerTest.java index deb6f71c695b..8da927dcb4ab 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/UiAutomationManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/UiAutomationManagerTest.java @@ -56,7 +56,6 @@ public class UiAutomationManagerTest { MessageCapturingHandler mMessageCapturingHandler; - @Mock AccessibilityManagerService.UserState mMockUserState; @Mock Context mMockContext; @Mock AccessibilityServiceInfo mMockServiceInfo; @Mock ResolveInfo mMockResolveInfo; diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java index 9a1fd9cf0e12..c3ef832a7f61 100644 --- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java @@ -93,7 +93,7 @@ import android.util.Pair; import androidx.test.filters.SmallTest; import com.android.internal.R; -import com.android.internal.widget.LockPatternUtils; +import com.android.internal.widget.LockscreenCredential; import com.android.server.LocalServices; import com.android.server.SystemService; import com.android.server.devicepolicy.DevicePolicyManagerService.RestrictionsListener; @@ -4266,9 +4266,9 @@ public class DevicePolicyManagerTest extends DpmTestBase { assertTrue(dpm.isResetPasswordTokenActive(admin1)); // test reset password with token - when(getServices().lockPatternUtils.setLockCredentialWithToken(eq(password.getBytes()), - eq(LockPatternUtils.CREDENTIAL_TYPE_PASSWORD), - eq(DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC), eq(handle), eq(token), + when(getServices().lockPatternUtils.setLockCredentialWithToken( + eq(LockscreenCredential.createPassword(password)), + eq(handle), eq(token), eq(UserHandle.USER_SYSTEM))) .thenReturn(true); assertTrue(dpm.resetPasswordWithToken(admin1, password, token, 0)); diff --git a/services/tests/servicestests/src/com/android/server/locksettings/BaseLockSettingsServiceTests.java b/services/tests/servicestests/src/com/android/server/locksettings/BaseLockSettingsServiceTests.java index f9ac02271a27..537287d18cca 100644 --- a/services/tests/servicestests/src/com/android/server/locksettings/BaseLockSettingsServiceTests.java +++ b/services/tests/servicestests/src/com/android/server/locksettings/BaseLockSettingsServiceTests.java @@ -35,7 +35,9 @@ import android.content.ComponentName; import android.content.pm.PackageManager; import android.content.pm.UserInfo; import android.hardware.authsecret.V1_0.IAuthSecret; +import android.hardware.face.Face; import android.hardware.face.FaceManager; +import android.hardware.fingerprint.Fingerprint; import android.hardware.fingerprint.FingerprintManager; import android.os.FileUtils; import android.os.IProgressListener; @@ -249,11 +251,32 @@ public abstract class BaseLockSettingsServiceTests extends AndroidTestCase { when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT)).thenReturn(true); when(mFingerprintManager.isHardwareDetected()).thenReturn(true); when(mFingerprintManager.hasEnrolledFingerprints(userId)).thenReturn(true); + doAnswer(new Answer<Void>() { + @Override + public Void answer(InvocationOnMock invocation) throws Throwable { + Fingerprint fp = (Fingerprint) invocation.getArguments()[0]; + FingerprintManager.RemovalCallback callback = + (FingerprintManager.RemovalCallback) invocation.getArguments()[2]; + callback.onRemovalSucceeded(fp, 0); + return null; + } + }).when(mFingerprintManager).remove(any(), eq(userId), any()); + // Hardware must be detected and templates must be enrolled when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_FACE)).thenReturn(true); when(mFaceManager.isHardwareDetected()).thenReturn(true); when(mFaceManager.hasEnrolledTemplates(userId)).thenReturn(true); + doAnswer(new Answer<Void>() { + @Override + public Void answer(InvocationOnMock invocation) throws Throwable { + Face face = (Face) invocation.getArguments()[0]; + FaceManager.RemovalCallback callback = + (FaceManager.RemovalCallback) invocation.getArguments()[2]; + callback.onRemovalSucceeded(face, 0); + return null; + } + }).when(mFaceManager).remove(any(), eq(userId), any()); } @Override diff --git a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsShellCommandTest.java b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsShellCommandTest.java index c00d33b431c3..b60111ea3333 100644 --- a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsShellCommandTest.java +++ b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsShellCommandTest.java @@ -19,10 +19,9 @@ package com.android.server.locksettings; import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC; import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_NUMERIC; -import static com.android.internal.widget.LockPatternUtils.stringToPattern; - import static junit.framework.Assert.assertEquals; +import static org.mockito.Matchers.anyBoolean; import static org.mockito.Matchers.anyInt; import static org.mockito.Mockito.any; import static org.mockito.Mockito.never; @@ -48,6 +47,8 @@ import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; import com.android.internal.widget.LockPatternUtils; +import com.android.internal.widget.LockPatternView; +import com.android.internal.widget.LockscreenCredential; import org.junit.Before; import org.junit.Test; @@ -55,6 +56,8 @@ import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import java.util.List; + /** * Test class for {@link LockSettingsShellCommand}. * @@ -87,24 +90,30 @@ public class LockSettingsShellCommandTest { public void testWrongPassword() throws Exception { when(mLockPatternUtils.isLockPatternEnabled(mUserId)).thenReturn(false); when(mLockPatternUtils.isLockPasswordEnabled(mUserId)).thenReturn(true); - when(mLockPatternUtils.checkPassword("1234".getBytes(), mUserId)).thenReturn(false); + when(mLockPatternUtils.checkCredential( + LockscreenCredential.createPassword("1234"), mUserId, null)).thenReturn(false); assertEquals(-1, mCommand.exec(mBinder, in, out, err, new String[] { "set-pin", "--old", "1234" }, mShellCallback, mResultReceiver)); - verify(mLockPatternUtils, never()).saveLockPassword(any(byte[].class), any(byte[].class), - anyInt(), anyInt()); + verify(mLockPatternUtils, never()).setLockCredential(any(), any(), + anyInt(), anyBoolean()); } @Test public void testChangePin() throws Exception { when(mLockPatternUtils.isLockPatternEnabled(mUserId)).thenReturn(false); when(mLockPatternUtils.isLockPasswordEnabled(mUserId)).thenReturn(true); - when(mLockPatternUtils.checkPassword("1234".getBytes(), mUserId)).thenReturn(true); + when(mLockPatternUtils.getKeyguardStoredPasswordQuality(mUserId)).thenReturn( + PASSWORD_QUALITY_NUMERIC); + when(mLockPatternUtils.checkCredential( + LockscreenCredential.createPin("1234"), mUserId, null)).thenReturn(true); assertEquals(0, mCommand.exec(new Binder(), in, out, err, new String[] { "set-pin", "--old", "1234", "4321" }, mShellCallback, mResultReceiver)); - verify(mLockPatternUtils).saveLockPassword("4321".getBytes(), "1234".getBytes(), - PASSWORD_QUALITY_NUMERIC, mUserId); + verify(mLockPatternUtils).setLockCredential( + LockscreenCredential.createPin("4321"), + LockscreenCredential.createPin("1234"), + mUserId); } @Test @@ -121,12 +130,17 @@ public class LockSettingsShellCommandTest { public void testChangePassword() throws Exception { when(mLockPatternUtils.isLockPatternEnabled(mUserId)).thenReturn(false); when(mLockPatternUtils.isLockPasswordEnabled(mUserId)).thenReturn(true); - when(mLockPatternUtils.checkPassword("1234".getBytes(), mUserId)).thenReturn(true); + when(mLockPatternUtils.getKeyguardStoredPasswordQuality(mUserId)).thenReturn( + PASSWORD_QUALITY_ALPHABETIC); + when(mLockPatternUtils.checkCredential( + LockscreenCredential.createPassword("1234"), mUserId, null)).thenReturn(true); assertEquals(0, mCommand.exec(new Binder(), in, out, err, new String[] { "set-password", "--old", "1234", "4321" }, mShellCallback, mResultReceiver)); - verify(mLockPatternUtils).saveLockPassword("4321".getBytes(), "1234".getBytes(), - PASSWORD_QUALITY_ALPHABETIC, mUserId); + verify(mLockPatternUtils).setLockCredential( + LockscreenCredential.createPassword("4321"), + LockscreenCredential.createPassword("1234"), + mUserId); } @Test @@ -143,11 +157,15 @@ public class LockSettingsShellCommandTest { public void testChangePattern() throws Exception { when(mLockPatternUtils.isLockPatternEnabled(mUserId)).thenReturn(true); when(mLockPatternUtils.isLockPasswordEnabled(mUserId)).thenReturn(false); - when(mLockPatternUtils.checkPattern(stringToPattern("1234"), mUserId)).thenReturn(true); + when(mLockPatternUtils.checkCredential( + LockscreenCredential.createPattern(stringToPattern("1234")), + mUserId, null)).thenReturn(true); assertEquals(0, mCommand.exec(new Binder(), in, out, err, new String[] { "set-pattern", "--old", "1234", "4321" }, mShellCallback, mResultReceiver)); - verify(mLockPatternUtils).saveLockPattern(stringToPattern("4321"), "1234".getBytes(), + verify(mLockPatternUtils).setLockCredential( + LockscreenCredential.createPattern(stringToPattern("4321")), + LockscreenCredential.createPattern(stringToPattern("1234")), mUserId); } @@ -165,10 +183,19 @@ public class LockSettingsShellCommandTest { public void testClear() throws Exception { when(mLockPatternUtils.isLockPatternEnabled(mUserId)).thenReturn(true); when(mLockPatternUtils.isLockPasswordEnabled(mUserId)).thenReturn(false); - when(mLockPatternUtils.checkPattern(stringToPattern("1234"), mUserId)).thenReturn(true); + when(mLockPatternUtils.checkCredential( + LockscreenCredential.createPattern(stringToPattern("1234")), + mUserId, null)).thenReturn(true); assertEquals(0, mCommand.exec(new Binder(), in, out, err, new String[] { "clear", "--old", "1234" }, mShellCallback, mResultReceiver)); - verify(mLockPatternUtils).clearLock("1234".getBytes(), mUserId); + verify(mLockPatternUtils).setLockCredential( + LockscreenCredential.createNone(), + LockscreenCredential.createPattern(stringToPattern("1234")), + mUserId); + } + + private List<LockPatternView.Cell> stringToPattern(String str) { + return LockPatternUtils.byteArrayToPattern(str.getBytes()); } } diff --git a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsStorageTests.java b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsStorageTests.java index 2a169b775ca3..cb5189712685 100644 --- a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsStorageTests.java +++ b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsStorageTests.java @@ -434,35 +434,6 @@ public class LockSettingsStorageTests extends AndroidTestCase { assertEquals(2, PersistentData.TYPE_SP_WEAVER); } - public void testCredentialHash_serializeUnserialize() { - byte[] serialized = CredentialHash.create( - PAYLOAD, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD).toBytes(); - CredentialHash deserialized = CredentialHash.fromBytes(serialized); - - assertEquals(LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, deserialized.type); - assertArrayEquals(PAYLOAD, deserialized.hash); - } - - public void testCredentialHash_unserialize_versionGatekeeper() { - // This test ensures that we can read serialized VERSION_GATEKEEPER CredentialHashes - // even if we change the wire format in the future. - byte[] serialized = new byte[] { - 1, /* VERSION_GATEKEEPER */ - 2, /* CREDENTIAL_TYPE_PASSWORD */ - 0, 0, 0, 5, /* hash length */ - 1, 2, -1, -2, 33, /* hash */ - }; - CredentialHash deserialized = CredentialHash.fromBytes(serialized); - - assertEquals(LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, deserialized.type); - assertArrayEquals(PAYLOAD, deserialized.hash); - - // Make sure the constants we use on the wire do not change. - assertEquals(-1, LockPatternUtils.CREDENTIAL_TYPE_NONE); - assertEquals(1, LockPatternUtils.CREDENTIAL_TYPE_PATTERN); - assertEquals(2, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD); - } - private static void assertArrayEquals(byte[] expected, byte[] actual) { if (!Arrays.equals(expected, actual)) { fail("expected:<" + Arrays.toString(expected) + diff --git a/services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java b/services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java index 0776589ea47f..42ca42aecf70 100644 --- a/services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java +++ b/services/tests/servicestests/src/com/android/server/locksettings/SyntheticPasswordTests.java @@ -39,6 +39,7 @@ import android.platform.test.annotations.Presubmit; import androidx.test.filters.SmallTest; import com.android.internal.widget.LockPatternUtils; +import com.android.internal.widget.LockscreenCredential; import com.android.internal.widget.VerifyCredentialResponse; import com.android.server.locksettings.SyntheticPasswordManager.AuthenticationResult; import com.android.server.locksettings.SyntheticPasswordManager.AuthenticationToken; @@ -364,7 +365,7 @@ public class SyntheticPasswordTests extends BaseLockSettingsServiceTests { // Verify DPM gets notified about new device lock flushHandlerTasks(); final PasswordMetrics metric = PasswordMetrics.computeForCredential( - LockPatternUtils.CREDENTIAL_TYPE_PATTERN, pattern); + LockscreenCredential.createPattern(LockPatternUtils.byteArrayToPattern(pattern))); assertEquals(metric, mService.getUserPasswordMetrics(PRIMARY_USER_ID)); verify(mDevicePolicyManager).reportPasswordChanged(PRIMARY_USER_ID); @@ -512,7 +513,7 @@ public class SyntheticPasswordTests extends BaseLockSettingsServiceTests { assertFalse(mService.havePattern(PRIMARY_USER_ID)); } - public void testgetHashFactorPrimaryUser() throws RemoteException { + public void testGetHashFactorPrimaryUser() throws RemoteException { final byte[] password = "password".getBytes(); mService.setLockCredential(password, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, null, PASSWORD_QUALITY_ALPHABETIC, PRIMARY_USER_ID, false); @@ -527,7 +528,7 @@ public class SyntheticPasswordTests extends BaseLockSettingsServiceTests { assertArrayEquals(hashFactor, newHashFactor); } - public void testgetHashFactorManagedProfileUnifiedChallenge() throws RemoteException { + public void testGetHashFactorManagedProfileUnifiedChallenge() throws RemoteException { final byte[] pattern = "1236".getBytes(); mService.setLockCredential(pattern, LockPatternUtils.CREDENTIAL_TYPE_PATTERN, null, PASSWORD_QUALITY_SOMETHING, PRIMARY_USER_ID, false); @@ -535,7 +536,7 @@ public class SyntheticPasswordTests extends BaseLockSettingsServiceTests { assertNotNull(mService.getHashFactor(null, MANAGED_PROFILE_USER_ID)); } - public void testgetHashFactorManagedProfileSeparateChallenge() throws RemoteException { + public void testGetHashFactorManagedProfileSeparateChallenge() throws RemoteException { final byte[] primaryPassword = "primary".getBytes(); final byte[] profilePassword = "profile".getBytes(); mService.setLockCredential(primaryPassword, LockPatternUtils.CREDENTIAL_TYPE_PASSWORD, null, diff --git a/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java index ba12b7393048..8a489047f179 100644 --- a/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java @@ -113,6 +113,7 @@ import android.net.NetworkStatsHistory; import android.net.NetworkTemplate; import android.net.StringNetworkSpecifier; import android.os.Binder; +import android.os.Handler; import android.os.INetworkManagementService; import android.os.PersistableBundle; import android.os.PowerManagerInternal; @@ -1117,7 +1118,7 @@ public class NetworkPolicyManagerServiceTest { // Define simple data plan final SubscriptionPlan plan = buildMonthlyDataPlan( ZonedDateTime.parse("2015-11-01T00:00:00.00Z"), DataUnit.MEGABYTES.toBytes(1800)); - mService.setSubscriptionPlans(TEST_SUB_ID, new SubscriptionPlan[] { plan }, + setSubscriptionPlans(TEST_SUB_ID, new SubscriptionPlan[] { plan }, mServiceContext.getOpPackageName()); // We're 20% through the month (6 days) @@ -1241,7 +1242,7 @@ public class NetworkPolicyManagerServiceTest { // Define simple data plan which gives us effectively 60MB/day final SubscriptionPlan plan = buildMonthlyDataPlan( ZonedDateTime.parse("2015-11-01T00:00:00.00Z"), DataUnit.MEGABYTES.toBytes(1800)); - mService.setSubscriptionPlans(TEST_SUB_ID, new SubscriptionPlan[] { plan }, + setSubscriptionPlans(TEST_SUB_ID, new SubscriptionPlan[] { plan }, mServiceContext.getOpPackageName()); // We're 20% through the month (6 days) @@ -1457,6 +1458,8 @@ public class NetworkPolicyManagerServiceTest { when(mConnManager.getAllNetworkState()).thenReturn(new NetworkState[0]); when(mSubscriptionManager.getActiveSubscriptionIdList()).thenReturn(new int[]{FAKE_SUB_ID}); when(mTelephonyManager.getSubscriberId(FAKE_SUB_ID)).thenReturn(FAKE_SUBSCRIBER_ID); + when(mTelephonyManager.createForSubscriptionId(FAKE_SUB_ID)) + .thenReturn(mock(TelephonyManager.class)); PersistableBundle bundle = CarrierConfigManager.getDefaultConfig(); when(mCarrierConfigManager.getConfigForSubId(FAKE_SUB_ID)).thenReturn(bundle); setNetworkPolicies(buildDefaultFakeMobilePolicy()); @@ -1468,6 +1471,8 @@ public class NetworkPolicyManagerServiceTest { when(mConnManager.getAllNetworkState()).thenReturn(new NetworkState[0]); when(mSubscriptionManager.getActiveSubscriptionIdList()).thenReturn(new int[]{FAKE_SUB_ID}); when(mTelephonyManager.getSubscriberId(FAKE_SUB_ID)).thenReturn(FAKE_SUBSCRIBER_ID); + when(mTelephonyManager.createForSubscriptionId(FAKE_SUB_ID)) + .thenReturn(mock(TelephonyManager.class)); when(mCarrierConfigManager.getConfigForSubId(FAKE_SUB_ID)).thenReturn(null); setNetworkPolicies(buildDefaultFakeMobilePolicy()); // smoke test to make sure no errors are raised @@ -1653,7 +1658,7 @@ public class NetworkPolicyManagerServiceTest { final SubscriptionPlan plan = buildMonthlyDataPlan( ZonedDateTime.parse("2015-11-01T00:00:00.00Z"), DataUnit.MEGABYTES.toBytes(1800)); - mService.setSubscriptionPlans(TEST_SUB_ID, new SubscriptionPlan[]{plan}, + setSubscriptionPlans(TEST_SUB_ID, new SubscriptionPlan[]{plan}, mServiceContext.getOpPackageName()); reset(mTelephonyManager, mNetworkManager, mNotifManager); @@ -1674,7 +1679,7 @@ public class NetworkPolicyManagerServiceTest { final SubscriptionPlan plan = buildMonthlyDataPlan( ZonedDateTime.parse("2015-11-01T00:00:00.00Z"), DataUnit.MEGABYTES.toBytes(100)); - mService.setSubscriptionPlans(TEST_SUB_ID, new SubscriptionPlan[]{plan}, + setSubscriptionPlans(TEST_SUB_ID, new SubscriptionPlan[]{plan}, mServiceContext.getOpPackageName()); reset(mTelephonyManager, mNetworkManager, mNotifManager); @@ -1690,7 +1695,7 @@ public class NetworkPolicyManagerServiceTest { { final SubscriptionPlan plan = buildMonthlyDataPlan( ZonedDateTime.parse("2015-11-01T00:00:00.00Z"), BYTES_UNLIMITED); - mService.setSubscriptionPlans(TEST_SUB_ID, new SubscriptionPlan[]{plan}, + setSubscriptionPlans(TEST_SUB_ID, new SubscriptionPlan[]{plan}, mServiceContext.getOpPackageName()); reset(mTelephonyManager, mNetworkManager, mNotifManager); @@ -1707,7 +1712,7 @@ public class NetworkPolicyManagerServiceTest { { final SubscriptionPlan plan = buildMonthlyDataPlan( ZonedDateTime.parse("2015-11-01T00:00:00.00Z"), BYTES_UNLIMITED); - mService.setSubscriptionPlans(TEST_SUB_ID, new SubscriptionPlan[]{plan}, + setSubscriptionPlans(TEST_SUB_ID, new SubscriptionPlan[]{plan}, mServiceContext.getOpPackageName()); reset(mTelephonyManager, mNetworkManager, mNotifManager); @@ -1923,6 +1928,8 @@ public class NetworkPolicyManagerServiceTest { when(mSubscriptionManager.getActiveSubscriptionIdList()).thenReturn( new int[] { TEST_SUB_ID }); when(mTelephonyManager.getSubscriberId(TEST_SUB_ID)).thenReturn(TEST_IMSI); + when(mTelephonyManager.createForSubscriptionId(TEST_SUB_ID)) + .thenReturn(mock(TelephonyManager.class)); doNothing().when(mTelephonyManager).setPolicyDataEnabled(anyBoolean(), anyInt()); expectNetworkState(false /* roaming */); } @@ -2049,6 +2056,23 @@ public class NetworkPolicyManagerServiceTest { private FutureIntent mRestrictBackgroundChanged; + private void postMsgAndWaitForCompletion() throws InterruptedException { + final Handler handler = mService.getHandlerForTesting(); + final CountDownLatch latch = new CountDownLatch(1); + mService.getHandlerForTesting().post(latch::countDown); + if (!latch.await(5, TimeUnit.SECONDS)) { + fail("Timed out waiting for the test msg to be handled"); + } + } + + private void setSubscriptionPlans(int subId, SubscriptionPlan[] plans, String callingPackage) + throws InterruptedException { + mService.setSubscriptionPlans(subId, plans, callingPackage); + // setSubscriptionPlans() triggers async events, wait for those to be completed before + // moving forward as they could interfere with the tests later. + postMsgAndWaitForCompletion(); + } + private void setRestrictBackground(boolean flag) throws Exception { mService.setRestrictBackground(flag); // Sanity check. diff --git a/services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java b/services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java index 8c3373faa0d4..3ae5674e63a3 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/BuzzBeepBlinkTest.java @@ -22,6 +22,7 @@ import static android.app.NotificationManager.IMPORTANCE_HIGH; import static android.app.NotificationManager.IMPORTANCE_LOW; import static android.app.NotificationManager.IMPORTANCE_MIN; import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_LIGHTS; +import static android.media.AudioAttributes.USAGE_NOTIFICATION_RINGTONE; import static junit.framework.Assert.assertFalse; import static junit.framework.Assert.assertNull; @@ -105,6 +106,7 @@ public class BuzzBeepBlinkTest extends UiServiceTestCase { private int mUid = 1000; private int mPid = 2000; private android.os.UserHandle mUser = UserHandle.of(ActivityManager.getCurrentUser()); + private NotificationChannel mChannel; private VibrateRepeatMatcher mVibrateOnceMatcher = new VibrateRepeatMatcher(-1); private VibrateRepeatMatcher mVibrateLoopMatcher = new VibrateRepeatMatcher(0); @@ -158,6 +160,8 @@ public class BuzzBeepBlinkTest extends UiServiceTestCase { mService.mScreenOn = false; mService.mInCallStateOffHook = false; mService.mNotificationPulseEnabled = true; + + mChannel = new NotificationChannel("test", "test", IMPORTANCE_HIGH); } // @@ -174,13 +178,18 @@ public class BuzzBeepBlinkTest extends UiServiceTestCase { true /* noisy */, false /* buzzy*/, false /* lights */); } + private NotificationRecord getBeepyOtherNotification() { + return getNotificationRecord(mOtherId, false /* insistent */, false /* once */, + true /* noisy */, false /* buzzy*/, false /* lights */); + } + private NotificationRecord getBeepyOnceNotification() { return getNotificationRecord(mId, false /* insistent */, true /* once */, true /* noisy */, false /* buzzy*/, false /* lights */); } private NotificationRecord getQuietNotification() { - return getNotificationRecord(mId, false /* insistent */, false /* once */, + return getNotificationRecord(mId, false /* insistent */, true /* once */, false /* noisy */, false /* buzzy*/, false /* lights */); } @@ -214,6 +223,11 @@ public class BuzzBeepBlinkTest extends UiServiceTestCase { false /* noisy */, true /* buzzy*/, false /* lights */); } + private NotificationRecord getBuzzyOtherNotification() { + return getNotificationRecord(mOtherId, false /* insistent */, false /* once */, + false /* noisy */, true /* buzzy*/, false /* lights */); + } + private NotificationRecord getBuzzyOnceNotification() { return getNotificationRecord(mId, false /* insistent */, true /* once */, false /* noisy */, true /* buzzy*/, false /* lights */); @@ -239,22 +253,34 @@ public class BuzzBeepBlinkTest extends UiServiceTestCase { false /* noisy */, false /* buzzy*/, true /* lights */); } - private NotificationRecord getCallRecord(int id, boolean insistent) { - return getNotificationRecord(id, false, false /* once */, true /* noisy */, - false /* buzzy */, false /* lights */, false /* default vib */, - false /* default sound */, false /* default lights */, "", - Notification.GROUP_ALERT_ALL, false); + private NotificationRecord getCallRecord(int id, NotificationChannel channel, boolean looping) { + final Builder builder = new Builder(getContext()) + .setContentTitle("foo") + .setSmallIcon(android.R.drawable.sym_def_app_icon) + .setPriority(Notification.PRIORITY_HIGH); + Notification n = builder.build(); + if (looping) { + n.flags |= Notification.FLAG_INSISTENT; + } + StatusBarNotification sbn = new StatusBarNotification(mPkg, mPkg, id, mTag, mUid, + mPid, n, mUser, null, System.currentTimeMillis()); + NotificationRecord r = new NotificationRecord(getContext(), sbn, channel); + mService.addNotification(r); + + return r; } private NotificationRecord getNotificationRecord(int id, boolean insistent, boolean once, boolean noisy, boolean buzzy, boolean lights) { - return getNotificationRecord(id, insistent, once, noisy, buzzy, lights, true, true, true, - null, Notification.GROUP_ALERT_ALL, false); + return getNotificationRecord(id, insistent, once, noisy, buzzy, lights, buzzy, noisy, + lights, null, Notification.GROUP_ALERT_ALL, false); } - private NotificationRecord getLeanbackNotificationRecord(int id, boolean insistent, boolean once, + private NotificationRecord getLeanbackNotificationRecord(int id, boolean insistent, + boolean once, boolean noisy, boolean buzzy, boolean lights) { - return getNotificationRecord(id, insistent, once, noisy, buzzy, lights, true, true, true, + return getNotificationRecord(id, insistent, once, noisy, buzzy, lights, true, true, + true, null, Notification.GROUP_ALERT_ALL, true); } @@ -265,16 +291,16 @@ public class BuzzBeepBlinkTest extends UiServiceTestCase { private NotificationRecord getLightsNotificationRecord(String groupKey, int groupAlertBehavior) { - return getNotificationRecord(mId, false, false, false, false, true /*lights*/, true, true, - true, groupKey, groupAlertBehavior, false); + return getNotificationRecord(mId, false, false, false, false, true /*lights*/, true, + true, true, groupKey, groupAlertBehavior, false); } - private NotificationRecord getNotificationRecord(int id, boolean insistent, boolean once, + private NotificationRecord getNotificationRecord(int id, + boolean insistent, boolean once, boolean noisy, boolean buzzy, boolean lights, boolean defaultVibration, boolean defaultSound, boolean defaultLights, String groupKey, int groupAlertBehavior, boolean isLeanback) { - NotificationChannel channel = - new NotificationChannel("test", "test", IMPORTANCE_HIGH); + final Builder builder = new Builder(getContext()) .setContentTitle("foo") .setSmallIcon(android.R.drawable.sym_def_app_icon) @@ -285,31 +311,37 @@ public class BuzzBeepBlinkTest extends UiServiceTestCase { if (noisy) { if (defaultSound) { defaults |= Notification.DEFAULT_SOUND; - channel.setSound(Settings.System.DEFAULT_NOTIFICATION_URI, + mChannel.setSound(Settings.System.DEFAULT_NOTIFICATION_URI, Notification.AUDIO_ATTRIBUTES_DEFAULT); } else { builder.setSound(CUSTOM_SOUND); - channel.setSound(CUSTOM_SOUND, CUSTOM_ATTRIBUTES); + mChannel.setSound(CUSTOM_SOUND, CUSTOM_ATTRIBUTES); } } else { - channel.setSound(null, null); + mChannel.setSound(null, null); } if (buzzy) { if (defaultVibration) { defaults |= Notification.DEFAULT_VIBRATE; } else { builder.setVibrate(CUSTOM_VIBRATION); - channel.setVibrationPattern(CUSTOM_VIBRATION); + mChannel.setVibrationPattern(CUSTOM_VIBRATION); } - channel.enableVibration(true); + mChannel.enableVibration(true); + } else { + mChannel.setVibrationPattern(null); + mChannel.enableVibration(false); } + if (lights) { if (defaultLights) { defaults |= Notification.DEFAULT_LIGHTS; } else { builder.setLights(CUSTOM_LIGHT_COLOR, CUSTOM_LIGHT_ON, CUSTOM_LIGHT_OFF); } - channel.enableLights(true); + mChannel.enableLights(true); + } else { + mChannel.enableLights(false); } builder.setDefaults(defaults); @@ -329,7 +361,7 @@ public class BuzzBeepBlinkTest extends UiServiceTestCase { StatusBarNotification sbn = new StatusBarNotification(mPkg, mPkg, id, mTag, mUid, mPid, n, mUser, null, System.currentTimeMillis()); - NotificationRecord r = new NotificationRecord(context, sbn, channel); + NotificationRecord r = new NotificationRecord(context, sbn, mChannel); mService.addNotification(r); return r; } @@ -339,18 +371,19 @@ public class BuzzBeepBlinkTest extends UiServiceTestCase { // private void verifyNeverBeep() throws RemoteException { - verify(mRingtonePlayer, never()).playAsync((Uri) anyObject(), (UserHandle) anyObject(), - anyBoolean(), (AudioAttributes) anyObject()); + verify(mRingtonePlayer, never()).playAsync(any(), any(), anyBoolean(), any()); } - private void verifyBeep() throws RemoteException { - verify(mRingtonePlayer, times(1)).playAsync((Uri) anyObject(), (UserHandle) anyObject(), - eq(true), (AudioAttributes) anyObject()); + private void verifyBeepUnlooped() throws RemoteException { + verify(mRingtonePlayer, times(1)).playAsync(any(), any(), eq(false), any()); } - private void verifyBeepLooped() throws RemoteException { - verify(mRingtonePlayer, times(1)).playAsync((Uri) anyObject(), (UserHandle) anyObject(), - eq(false), (AudioAttributes) anyObject()); + private void verifyBeepLooped() throws RemoteException { + verify(mRingtonePlayer, times(1)).playAsync(any(), any(), eq(true), any()); + } + + private void verifyBeep(int times) throws RemoteException { + verify(mRingtonePlayer, times(times)).playAsync(any(), any(), anyBoolean(), any()); } private void verifyNeverStopAudio() throws RemoteException { @@ -362,24 +395,31 @@ public class BuzzBeepBlinkTest extends UiServiceTestCase { } private void verifyNeverVibrate() { - verify(mVibrator, never()).vibrate(anyInt(), anyString(), (VibrationEffect) anyObject(), - anyString(), (AudioAttributes) anyObject()); + verify(mVibrator, never()).vibrate(anyInt(), anyString(), any(), anyString(), any()); } private void verifyVibrate() { verify(mVibrator, times(1)).vibrate(anyInt(), anyString(), argThat(mVibrateOnceMatcher), - anyString(), (AudioAttributes) anyObject()); + anyString(), any()); + } + + private void verifyVibrate(int times) { + verify(mVibrator, times(times)).vibrate(anyInt(), anyString(), any(), anyString(), any()); } private void verifyVibrateLooped() { verify(mVibrator, times(1)).vibrate(anyInt(), anyString(), argThat(mVibrateLoopMatcher), - anyString(), (AudioAttributes) anyObject()); + anyString(), any()); } private void verifyDelayedVibrateLooped() { verify(mVibrator, timeout(MAX_VIBRATION_DELAY).times(1)).vibrate(anyInt(), anyString(), - argThat(mVibrateLoopMatcher), anyString(), - (AudioAttributes) anyObject()); + argThat(mVibrateLoopMatcher), anyString(), any()); + } + + private void verifyDelayedVibrate() { + verify(mVibrator, timeout(MAX_VIBRATION_DELAY).times(1)).vibrate(anyInt(), anyString(), + argThat(mVibrateOnceMatcher), anyString(), any()); } private void verifyStopVibrate() { @@ -398,11 +438,6 @@ public class BuzzBeepBlinkTest extends UiServiceTestCase { verify(mLight, times(1)).setFlashing(anyInt(), anyInt(), anyInt(), anyInt()); } - private void verifyCustomLights() { - verify(mLight, times(1)).setFlashing( - eq(CUSTOM_LIGHT_COLOR), anyInt(), eq(CUSTOM_LIGHT_ON), eq(CUSTOM_LIGHT_OFF)); - } - // // Tests // @@ -425,7 +460,7 @@ public class BuzzBeepBlinkTest extends UiServiceTestCase { mService.buzzBeepBlinkLocked(r); - verifyBeepLooped(); + verifyBeepUnlooped(); verifyNeverVibrate(); verify(mAccessibilityService, times(1)).sendAccessibilityEvent(any(), anyInt()); assertTrue(r.isInterruptive()); @@ -438,7 +473,7 @@ public class BuzzBeepBlinkTest extends UiServiceTestCase { mService.buzzBeepBlinkLocked(r); - verifyBeep(); + verifyBeepLooped(); assertTrue(r.isInterruptive()); assertNotEquals(-1, r.getLastAudiblyAlertedMs()); } @@ -492,7 +527,7 @@ public class BuzzBeepBlinkTest extends UiServiceTestCase { mService.buzzBeepBlinkLocked(r); - verifyBeepLooped(); + verifyBeepUnlooped(); assertTrue(r.isInterruptive()); } @@ -533,7 +568,7 @@ public class BuzzBeepBlinkTest extends UiServiceTestCase { // update should beep r.isUpdate = true; mService.buzzBeepBlinkLocked(r); - verifyBeepLooped(); + verifyBeepUnlooped(); verify(mAccessibilityService, times(2)).sendAccessibilityEvent(any(), anyInt()); assertTrue(r.isInterruptive()); assertNotEquals(-1, r.getLastAudiblyAlertedMs()); @@ -723,7 +758,7 @@ public class BuzzBeepBlinkTest extends UiServiceTestCase { mService.buzzBeepBlinkLocked(r); verifyNeverVibrate(); - verifyBeepLooped(); + verifyBeepUnlooped(); assertTrue(r.isInterruptive()); assertNotEquals(-1, r.getLastAudiblyAlertedMs()); } @@ -821,7 +856,7 @@ public class BuzzBeepBlinkTest extends UiServiceTestCase { mService.buzzBeepBlinkLocked(summary); - verifyBeepLooped(); + verifyBeepUnlooped(); // summaries are never interruptive for notification counts assertFalse(summary.isInterruptive()); assertNotEquals(-1, summary.getLastAudiblyAlertedMs()); @@ -833,7 +868,7 @@ public class BuzzBeepBlinkTest extends UiServiceTestCase { mService.buzzBeepBlinkLocked(nonGroup); - verifyBeepLooped(); + verifyBeepUnlooped(); assertTrue(nonGroup.isInterruptive()); assertNotEquals(-1, nonGroup.getLastAudiblyAlertedMs()); } @@ -856,7 +891,7 @@ public class BuzzBeepBlinkTest extends UiServiceTestCase { mService.buzzBeepBlinkLocked(child); - verifyBeepLooped(); + verifyBeepUnlooped(); assertTrue(child.isInterruptive()); assertNotEquals(-1, child.getLastAudiblyAlertedMs()); } @@ -867,7 +902,7 @@ public class BuzzBeepBlinkTest extends UiServiceTestCase { mService.buzzBeepBlinkLocked(nonGroup); - verifyBeepLooped(); + verifyBeepUnlooped(); assertTrue(nonGroup.isInterruptive()); assertNotEquals(-1, nonGroup.getLastAudiblyAlertedMs()); } @@ -878,7 +913,7 @@ public class BuzzBeepBlinkTest extends UiServiceTestCase { mService.buzzBeepBlinkLocked(group); - verifyBeepLooped(); + verifyBeepUnlooped(); assertTrue(group.isInterruptive()); assertNotEquals(-1, group.getLastAudiblyAlertedMs()); } @@ -1293,7 +1328,11 @@ public class BuzzBeepBlinkTest extends UiServiceTestCase { @Test public void testListenerHintCall() throws Exception { - NotificationRecord r = getCallRecord(1, true); + NotificationChannel ringtoneChannel = + new NotificationChannel("ringtone", "", IMPORTANCE_HIGH); + ringtoneChannel.setSound(Settings.System.DEFAULT_RINGTONE_URI, + new AudioAttributes.Builder().setUsage(USAGE_NOTIFICATION_RINGTONE).build()); + NotificationRecord r = getCallRecord(1, ringtoneChannel, true); mService.setHints(NotificationListenerService.HINT_HOST_DISABLE_CALL_EFFECTS); @@ -1310,7 +1349,7 @@ public class BuzzBeepBlinkTest extends UiServiceTestCase { mService.buzzBeepBlinkLocked(r); - verifyBeepLooped(); + verifyBeepUnlooped(); } @Test @@ -1326,7 +1365,11 @@ public class BuzzBeepBlinkTest extends UiServiceTestCase { @Test public void testListenerHintBoth() throws Exception { - NotificationRecord r = getCallRecord(1, true); + NotificationChannel ringtoneChannel = + new NotificationChannel("ringtone", "", IMPORTANCE_HIGH); + ringtoneChannel.setSound(Settings.System.DEFAULT_RINGTONE_URI, + new AudioAttributes.Builder().setUsage(USAGE_NOTIFICATION_RINGTONE).build()); + NotificationRecord r = getCallRecord(1, ringtoneChannel, true); NotificationRecord s = getBeepyNotification(); mService.setHints(NotificationListenerService.HINT_HOST_DISABLE_NOTIFICATION_EFFECTS @@ -1340,7 +1383,11 @@ public class BuzzBeepBlinkTest extends UiServiceTestCase { @Test public void testListenerHintNotification_callSound() throws Exception { - NotificationRecord r = getCallRecord(1, true); + NotificationChannel ringtoneChannel = + new NotificationChannel("ringtone", "", IMPORTANCE_HIGH); + ringtoneChannel.setSound(Settings.System.DEFAULT_RINGTONE_URI, + new AudioAttributes.Builder().setUsage(USAGE_NOTIFICATION_RINGTONE).build()); + NotificationRecord r = getCallRecord(1, ringtoneChannel, true); mService.setHints(NotificationListenerService.HINT_HOST_DISABLE_NOTIFICATION_EFFECTS); @@ -1349,6 +1396,167 @@ public class BuzzBeepBlinkTest extends UiServiceTestCase { verifyBeepLooped(); } + @Test + public void testCannotInterruptRingtoneInsistentBeep() throws Exception { + NotificationChannel ringtoneChannel = + new NotificationChannel("ringtone", "", IMPORTANCE_HIGH); + ringtoneChannel.setSound(Settings.System.DEFAULT_RINGTONE_URI, + new AudioAttributes.Builder().setUsage(USAGE_NOTIFICATION_RINGTONE).build()); + NotificationRecord ringtoneNotification = getCallRecord(1, ringtoneChannel, true); + mService.addNotification(ringtoneNotification); + + mService.buzzBeepBlinkLocked(ringtoneNotification); + verifyBeepLooped(); + + NotificationRecord interrupter = getBeepyOtherNotification(); + assertTrue(mService.shouldMuteNotificationLocked(interrupter)); + mService.buzzBeepBlinkLocked(interrupter); + + verifyBeep(1); + + assertFalse(interrupter.isInterruptive()); + assertEquals(-1, interrupter.getLastAudiblyAlertedMs()); + } + + @Test + public void testCannotInterruptRingtoneInsistentBuzz() { + NotificationChannel ringtoneChannel = + new NotificationChannel("ringtone", "", IMPORTANCE_HIGH); + ringtoneChannel.setSound(Uri.EMPTY, + new AudioAttributes.Builder().setUsage(USAGE_NOTIFICATION_RINGTONE).build()); + ringtoneChannel.enableVibration(true); + NotificationRecord ringtoneNotification = getCallRecord(1, ringtoneChannel, true); + assertFalse(mService.shouldMuteNotificationLocked(ringtoneNotification)); + + mService.buzzBeepBlinkLocked(ringtoneNotification); + verifyVibrateLooped(); + + NotificationRecord interrupter = getBuzzyOtherNotification(); + assertTrue(mService.shouldMuteNotificationLocked(interrupter)); + mService.buzzBeepBlinkLocked(interrupter); + + verifyVibrate(1); + + assertFalse(interrupter.isInterruptive()); + assertEquals(-1, interrupter.getLastAudiblyAlertedMs()); + } + + @Test + public void testCanInterruptRingtoneNonInsistentBeep() throws Exception { + NotificationChannel ringtoneChannel = + new NotificationChannel("ringtone", "", IMPORTANCE_HIGH); + ringtoneChannel.setSound(Settings.System.DEFAULT_RINGTONE_URI, + new AudioAttributes.Builder().setUsage(USAGE_NOTIFICATION_RINGTONE).build()); + NotificationRecord ringtoneNotification = getCallRecord(1, ringtoneChannel, false); + + mService.buzzBeepBlinkLocked(ringtoneNotification); + verifyBeepUnlooped(); + + NotificationRecord interrupter = getBeepyOtherNotification(); + mService.buzzBeepBlinkLocked(interrupter); + + verifyBeep(2); + + assertTrue(interrupter.isInterruptive()); + } + + @Test + public void testCanInterruptRingtoneNonInsistentBuzz() { + NotificationChannel ringtoneChannel = + new NotificationChannel("ringtone", "", IMPORTANCE_HIGH); + ringtoneChannel.setSound(null, + new AudioAttributes.Builder().setUsage(USAGE_NOTIFICATION_RINGTONE).build()); + ringtoneChannel.enableVibration(true); + NotificationRecord ringtoneNotification = getCallRecord(1, ringtoneChannel, false); + + mService.buzzBeepBlinkLocked(ringtoneNotification); + verifyVibrate(); + + NotificationRecord interrupter = getBuzzyOtherNotification(); + mService.buzzBeepBlinkLocked(interrupter); + + verifyVibrate(2); + + assertTrue(interrupter.isInterruptive()); + } + + @Test + public void testRingtoneInsistentBeep_doesNotBlockFutureSoundsOnceStopped() throws Exception { + NotificationChannel ringtoneChannel = + new NotificationChannel("ringtone", "", IMPORTANCE_HIGH); + ringtoneChannel.setSound(Settings.System.DEFAULT_RINGTONE_URI, + new AudioAttributes.Builder().setUsage(USAGE_NOTIFICATION_RINGTONE).build()); + NotificationRecord ringtoneNotification = getCallRecord(1, ringtoneChannel, true); + + mService.buzzBeepBlinkLocked(ringtoneNotification); + verifyBeepLooped(); + + mService.clearSoundLocked(); + + NotificationRecord interrupter = getBeepyOtherNotification(); + mService.buzzBeepBlinkLocked(interrupter); + + verifyBeep(2); + + assertTrue(interrupter.isInterruptive()); + } + + @Test + public void testRingtoneInsistentBuzz_doesNotBlockFutureSoundsOnceStopped() { + NotificationChannel ringtoneChannel = + new NotificationChannel("ringtone", "", IMPORTANCE_HIGH); + ringtoneChannel.setSound(null, + new AudioAttributes.Builder().setUsage(USAGE_NOTIFICATION_RINGTONE).build()); + ringtoneChannel.enableVibration(true); + NotificationRecord ringtoneNotification = getCallRecord(1, ringtoneChannel, true); + + mService.buzzBeepBlinkLocked(ringtoneNotification); + verifyVibrateLooped(); + + mService.clearVibrateLocked(); + + NotificationRecord interrupter = getBuzzyOtherNotification(); + mService.buzzBeepBlinkLocked(interrupter); + + verifyVibrate(2); + + assertTrue(interrupter.isInterruptive()); + } + + @Test + public void testCanInterruptNonRingtoneInsistentBeep() throws Exception { + NotificationChannel fakeRingtoneChannel = + new NotificationChannel("ringtone", "", IMPORTANCE_HIGH); + NotificationRecord ringtoneNotification = getCallRecord(1, fakeRingtoneChannel, true); + + mService.buzzBeepBlinkLocked(ringtoneNotification); + verifyBeepLooped(); + + NotificationRecord interrupter = getBeepyOtherNotification(); + mService.buzzBeepBlinkLocked(interrupter); + + verifyBeep(2); + + assertTrue(interrupter.isInterruptive()); + } + + @Test + public void testCanInterruptNonRingtoneInsistentBuzz() throws Exception { + NotificationChannel fakeRingtoneChannel = + new NotificationChannel("ringtone", "", IMPORTANCE_HIGH); + fakeRingtoneChannel.enableVibration(true); + NotificationRecord ringtoneNotification = getCallRecord(1, fakeRingtoneChannel, true); + + mService.buzzBeepBlinkLocked(ringtoneNotification); + + NotificationRecord interrupter = getBuzzyOtherNotification(); + mService.buzzBeepBlinkLocked(interrupter); + + verifyVibrate(2); + + assertTrue(interrupter.isInterruptive()); + } + static class VibrateRepeatMatcher implements ArgumentMatcher<VibrationEffect> { private final int mRepeatIndex; 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 4ea2fc0d398e..cd0f4f185927 100755 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java @@ -537,6 +537,18 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { return new NotificationRecord(mContext, sbn, channel); } + private NotificationRecord generateNotificationRecord(NotificationChannel channel, int userId) { + if (channel == null) { + channel = mTestNotificationChannel; + } + Notification.Builder nb = new Notification.Builder(mContext, channel.getId()) + .setContentTitle("foo") + .setSmallIcon(android.R.drawable.sym_def_app_icon); + StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, 1, "tag", mUid, 0, + nb.build(), new UserHandle(userId), null, 0); + return new NotificationRecord(mContext, sbn, channel); + } + private Map<String, Answer> getSignalExtractorSideEffects() { Map<String, Answer> answers = new ArrayMap<>(); @@ -5451,6 +5463,112 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { } @Test + public void testGrantInlineReplyUriPermission_recordExists() throws Exception { + NotificationRecord nr = generateNotificationRecord(mTestNotificationChannel, 0); + mBinderService.enqueueNotificationWithTag(PKG, PKG, "tag", + nr.sbn.getId(), nr.sbn.getNotification(), nr.sbn.getUserId()); + waitForIdle(); + + // A notification exists for the given record + StatusBarNotification[] notifsBefore = mBinderService.getActiveNotifications(PKG); + assertEquals(1, notifsBefore.length); + + reset(mPackageManager); + + Uri uri = ContentUris.withAppendedId(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, 1); + + mService.mNotificationDelegate.grantInlineReplyUriPermission( + nr.getKey(), uri, nr.sbn.getUid()); + + // Grant permission called for the UID of SystemUI under the target user ID + verify(mUgm, times(1)).grantUriPermissionFromOwner(any(), + eq(nr.sbn.getUid()), eq(nr.sbn.getPackageName()), eq(uri), anyInt(), anyInt(), + eq(nr.sbn.getUserId())); + } + + @Test + public void testGrantInlineReplyUriPermission_userAll() throws Exception { + // generate a NotificationRecord for USER_ALL to make sure it's converted into USER_SYSTEM + NotificationRecord nr = + generateNotificationRecord(mTestNotificationChannel, UserHandle.USER_ALL); + mBinderService.enqueueNotificationWithTag(PKG, PKG, "tag", + nr.sbn.getId(), nr.sbn.getNotification(), nr.sbn.getUserId()); + waitForIdle(); + + // A notification exists for the given record + StatusBarNotification[] notifsBefore = mBinderService.getActiveNotifications(PKG); + assertEquals(1, notifsBefore.length); + + reset(mPackageManager); + + Uri uri = ContentUris.withAppendedId(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, 1); + + mService.mNotificationDelegate.grantInlineReplyUriPermission( + nr.getKey(), uri, nr.sbn.getUid()); + + // Target user for the grant is USER_ALL instead of USER_SYSTEM + verify(mUgm, times(1)).grantUriPermissionFromOwner(any(), + eq(nr.sbn.getUid()), eq(nr.sbn.getPackageName()), eq(uri), anyInt(), anyInt(), + eq(UserHandle.USER_SYSTEM)); + } + + @Test + public void testGrantInlineReplyUriPermission_acrossUsers() throws Exception { + // generate a NotificationRecord for USER_ALL to make sure it's converted into USER_SYSTEM + int otherUserId = 11; + NotificationRecord nr = + generateNotificationRecord(mTestNotificationChannel, otherUserId); + mBinderService.enqueueNotificationWithTag(PKG, PKG, "tag", + nr.sbn.getId(), nr.sbn.getNotification(), nr.sbn.getUserId()); + waitForIdle(); + + // A notification exists for the given record + StatusBarNotification[] notifsBefore = mBinderService.getActiveNotifications(PKG); + assertEquals(1, notifsBefore.length); + + reset(mPackageManager); + + Uri uri = ContentUris.withAppendedId(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, 1); + + int uid = 0; // sysui on primary user + int otherUserUid = (otherUserId * 100000) + 1; // SystemUI as a different user + String sysuiPackage = "sysui"; + final String[] sysuiPackages = new String[] { sysuiPackage }; + when(mPackageManager.getPackagesForUid(uid)).thenReturn(sysuiPackages); + + // Make sure to mock call for USER_SYSTEM and not USER_ALL, since it's been replaced by the + // time this is called + when(mPackageManager.getPackageUid(sysuiPackage, 0, otherUserId)) + .thenReturn(otherUserUid); + + mService.mNotificationDelegate.grantInlineReplyUriPermission(nr.getKey(), uri, uid); + + // Target user for the grant is USER_ALL instead of USER_SYSTEM + verify(mUgm, times(1)).grantUriPermissionFromOwner(any(), + eq(otherUserUid), eq(nr.sbn.getPackageName()), eq(uri), anyInt(), anyInt(), + eq(otherUserId)); + } + + @Test + public void testGrantInlineReplyUriPermission_noRecordExists() throws Exception { + NotificationRecord nr = generateNotificationRecord(mTestNotificationChannel); + waitForIdle(); + + // No notifications exist for the given record + StatusBarNotification[] notifsBefore = mBinderService.getActiveNotifications(PKG); + assertEquals(0, notifsBefore.length); + + Uri uri = ContentUris.withAppendedId(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, 1); + int uid = 0; // sysui on primary user + + mService.mNotificationDelegate.grantInlineReplyUriPermission(nr.getKey(), uri, uid); + + // Grant permission not called if no record exists for the given key + verify(mUgm, times(0)).grantUriPermissionFromOwner(any(), anyInt(), any(), + eq(uri), anyInt(), anyInt(), anyInt()); + } + + @Test public void testNotificationBubbles_disabled_lowRamDevice() throws Exception { // Bubbles are allowed! setUpPrefsForBubbles(true /* global */, true /* app */, true /* channel */); diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java index aaaa7a5ab596..2836e69f79da 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityMetricsLaunchObserverTests.java @@ -28,6 +28,7 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.verifyNoMor import static com.google.common.truth.Truth.assertWithMessage; import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.ArgumentMatchers.argThat; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.timeout; @@ -128,7 +129,7 @@ public class ActivityMetricsLaunchObserverTests extends ActivityTestsBase { mActivityMetricsLogger.notifyActivityLaunching(intent); - verifyAsync(mLaunchObserver).onIntentStarted(eq(intent)); + verifyAsync(mLaunchObserver).onIntentStarted(eq(intent), anyLong()); verifyNoMoreInteractions(mLaunchObserver); } @@ -163,12 +164,12 @@ public class ActivityMetricsLaunchObserverTests extends ActivityTestsBase { testOnActivityLaunched(); mActivityMetricsLogger.notifyTransitionStarting(new SparseIntArray(), - SystemClock.uptimeMillis()); + SystemClock.elapsedRealtimeNanos()); mActivityMetricsLogger.notifyWindowsDrawn(mActivityRecord.getWindowingMode(), - SystemClock.uptimeMillis()); + SystemClock.elapsedRealtimeNanos()); - verifyAsync(mLaunchObserver).onActivityLaunchFinished(eqProto(mActivityRecord)); + verifyAsync(mLaunchObserver).onActivityLaunchFinished(eqProto(mActivityRecord), anyLong()); verifyNoMoreInteractions(mLaunchObserver); } @@ -186,6 +187,16 @@ public class ActivityMetricsLaunchObserverTests extends ActivityTestsBase { } @Test + public void testOnReportFullyDrawn() throws Exception { + testOnActivityLaunched(); + + mActivityMetricsLogger.logAppTransitionReportedDrawn(mActivityRecord, false); + + verifyAsync(mLaunchObserver).onReportFullyDrawn(eqProto(mActivityRecord), anyLong()); + verifyNoMoreInteractions(mLaunchObserver); + } + + @Test public void testOnActivityLaunchedTrampoline() throws Exception { testOnIntentStarted(); @@ -206,12 +217,13 @@ public class ActivityMetricsLaunchObserverTests extends ActivityTestsBase { testOnActivityLaunchedTrampoline(); mActivityMetricsLogger.notifyTransitionStarting(new SparseIntArray(), - SystemClock.uptimeMillis()); + SystemClock.elapsedRealtimeNanos()); mActivityMetricsLogger.notifyWindowsDrawn(mActivityRecordTrampoline.getWindowingMode(), - SystemClock.uptimeMillis()); + SystemClock.elapsedRealtimeNanos()); - verifyAsync(mLaunchObserver).onActivityLaunchFinished(eqProto(mActivityRecordTrampoline)); + verifyAsync(mLaunchObserver).onActivityLaunchFinished(eqProto(mActivityRecordTrampoline), + anyLong()); verifyNoMoreInteractions(mLaunchObserver); } diff --git a/services/tests/wmtests/src/com/android/server/wm/TestIWindow.java b/services/tests/wmtests/src/com/android/server/wm/TestIWindow.java index 09e5027c1faa..6a9413716a89 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TestIWindow.java +++ b/services/tests/wmtests/src/com/android/server/wm/TestIWindow.java @@ -115,4 +115,8 @@ public class TestIWindow extends IWindow.Stub { @Override public void showInsets(int types, boolean fromIme) throws RemoteException { } + + @Override + public void hideInsets(int types, boolean fromIme) throws RemoteException { + } } diff --git a/startop/apps/test/Android.bp b/startop/apps/test/Android.bp index d949f74bec70..2ff26b8a5cde 100644 --- a/startop/apps/test/Android.bp +++ b/startop/apps/test/Android.bp @@ -17,11 +17,12 @@ android_app { name: "startop_test_app", srcs: [ + "src/ComplexLayoutInflationActivity.java", "src/CPUIntensive.java", "src/EmptyActivity.java", - "src/LayoutInflationActivity.java", - "src/ComplexLayoutInflationActivity.java", "src/FrameLayoutInflationActivity.java", + "src/LayoutInflationActivity.java", + "src/NonInteractiveSystemServerBenchmarkActivity.java", "src/SystemServerBenchmarkActivity.java", "src/SystemServerBenchmarks.java", "src/TextViewInflationActivity.java", diff --git a/startop/apps/test/AndroidManifest.xml b/startop/apps/test/AndroidManifest.xml index 15785d4d44b9..a5ac348f42d1 100644 --- a/startop/apps/test/AndroidManifest.xml +++ b/startop/apps/test/AndroidManifest.xml @@ -84,6 +84,13 @@ </intent-filter> </activity> + <activity + android:label="Non-interactive SystemServer Benchmark" + android:name=".NonInteractiveSystemServerBenchmarkActivity" + android:exported="true" /> + </application> + <uses-permission android:name="android.permission.WAKE_LOCK" /> + </manifest> diff --git a/startop/apps/test/README.md b/startop/apps/test/README.md index dadc66af3306..949dff75b723 100644 --- a/startop/apps/test/README.md +++ b/startop/apps/test/README.md @@ -24,3 +24,14 @@ spent in view inflation to make it easier to focus on the time spent in view inflation. adb shell am start -n com.android.startop.test/.ComplexLayoutInflationActivity + +## NonInteractiveSystemServerBenchmark + +This activity is for running microbenchmarks from the command line. Run as follows: + + adb shell am start -W -n com.android.startop.test .NonInteractiveSystemServerBenchmarkActivity + +It takes awhile (and there's currently no automated way to make sure it's done), +but when it finishes, you can get the results like this: + + adb shell cat /sdcard/Android/data/com.android.startop.test/files/benchmark.csv diff --git a/startop/apps/test/src/NonInteractiveSystemServerBenchmarkActivity.java b/startop/apps/test/src/NonInteractiveSystemServerBenchmarkActivity.java new file mode 100644 index 000000000000..a2dc2cf03d69 --- /dev/null +++ b/startop/apps/test/src/NonInteractiveSystemServerBenchmarkActivity.java @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.startop.test; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.PrintStream; +import java.util.ArrayList; + +import android.app.Activity; +import android.app.ActivityManager; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager; +import android.content.pm.PackageManager.NameNotFoundException; +import android.os.AsyncTask; +import android.os.Bundle; +import android.view.ViewGroup; +import android.widget.Button; +import android.widget.GridLayout; +import android.widget.TextView; + +public class NonInteractiveSystemServerBenchmarkActivity extends Activity { + ArrayList<CharSequence> benchmarkNames = new ArrayList(); + ArrayList<Runnable> benchmarkThunks = new ArrayList(); + + PrintStream out; + + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + SystemServerBenchmarks.initializeBenchmarks(this, (name, thunk) -> { + benchmarkNames.add(name); + benchmarkThunks.add(thunk); + }); + + try { + out = new PrintStream(new File(getExternalFilesDir(null), "benchmark.csv")); + } catch (FileNotFoundException e) { + throw new RuntimeException(e); + } + out.println("Name,Mean,Stdev"); + runBenchmarks(0); + } + + void runBenchmarks(int i) { + if (i < benchmarkNames.size()) { + SystemServerBenchmarks.runBenchmarkInBackground(benchmarkThunks.get(i), + (mean, stdev) -> { + out.printf("%s,%.0f,%.0f\n", benchmarkNames.get(i), mean, stdev); + runBenchmarks(i + 1); + }); + } + } +} diff --git a/startop/apps/test/src/SystemServerBenchmarks.java b/startop/apps/test/src/SystemServerBenchmarks.java index 818f1788335e..126c2c8f2db7 100644 --- a/startop/apps/test/src/SystemServerBenchmarks.java +++ b/startop/apps/test/src/SystemServerBenchmarks.java @@ -18,6 +18,7 @@ package com.android.startop.test; import android.app.Activity; import android.app.ActivityManager; +import android.app.PendingIntent; import android.content.ComponentName; import android.content.Context; import android.content.Intent; @@ -25,6 +26,7 @@ import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; import android.os.AsyncTask; +import android.os.PowerManager; /** * An interface for running benchmarks and collecting results. Used so we can have both an @@ -48,7 +50,7 @@ class SystemServerBenchmarks { // Time limit to run benchmarks in seconds public static final int TIME_LIMIT = 5; - static void initializeBenchmarks(BenchmarkRunner benchmarks, Activity parent) { + static void initializeBenchmarks(Activity parent, BenchmarkRunner benchmarks) { benchmarks.addBenchmark("Empty", () -> { }); @@ -158,6 +160,21 @@ class SystemServerBenchmarks { benchmarks.addBenchmark("getRunningAppProcesses", () -> { am.getRunningAppProcesses(); }); + + // We use PendingIntent.getCreatorPackage, since + // getPackageIntentForSender is not public to us, but getCreatorPackage + // is just a thin wrapper around it. + PendingIntent pi = PendingIntent.getActivity(parent, 0, new Intent(), 0); + benchmarks.addBenchmark("getPackageIntentForSender", () -> { + pi.getCreatorPackage(); + }); + + PowerManager pwr = (PowerManager) parent.getSystemService(Context.POWER_SERVICE); + PowerManager.WakeLock wl = pwr.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "benchmark tag"); + benchmarks.addBenchmark("WakeLock Acquire/Release", () -> { + wl.acquire(); + wl.release(); + }); } /** diff --git a/startop/iorap/src/com/google/android/startop/iorap/AppLaunchEvent.java b/startop/iorap/src/com/google/android/startop/iorap/AppLaunchEvent.java index acf994610182..cf120cf687fb 100644 --- a/startop/iorap/src/com/google/android/startop/iorap/AppLaunchEvent.java +++ b/startop/iorap/src/com/google/android/startop/iorap/AppLaunchEvent.java @@ -86,10 +86,14 @@ public abstract class AppLaunchEvent implements Parcelable { public static final class IntentStarted extends AppLaunchEvent { @NonNull public final Intent intent; + public final long timestampNs; - public IntentStarted(@SequenceId long sequenceId, Intent intent) { + public IntentStarted(@SequenceId long sequenceId, + Intent intent, + long timestampNs) { super(sequenceId); this.intent = intent; + this.timestampNs = timestampNs; Objects.requireNonNull(intent, "intent"); } @@ -98,14 +102,16 @@ public abstract class AppLaunchEvent implements Parcelable { public boolean equals(Object other) { if (other instanceof IntentStarted) { return intent.equals(((IntentStarted)other).intent) && - super.equals(other); + timestampNs == ((IntentStarted)other).timestampNs && + super.equals(other); } return false; } @Override protected String toStringBody() { - return ", intent=" + intent.toString(); + return ", intent=" + intent.toString() + + " , timestampNs=" + Long.toString(timestampNs); } @@ -113,11 +119,13 @@ public abstract class AppLaunchEvent implements Parcelable { protected void writeToParcelImpl(Parcel p, int flags) { super.writeToParcelImpl(p, flags); IntentProtoParcelable.write(p, intent, flags); + p.writeLong(timestampNs); } IntentStarted(Parcel p) { super(p); intent = IntentProtoParcelable.create(p); + timestampNs = p.readLong(); } } @@ -216,18 +224,39 @@ public abstract class AppLaunchEvent implements Parcelable { } public static final class ActivityLaunchFinished extends BaseWithActivityRecordData { + public final long timestampNs; + public ActivityLaunchFinished(@SequenceId long sequenceId, - @NonNull @ActivityRecordProto byte[] snapshot) { + @NonNull @ActivityRecordProto byte[] snapshot, + long timestampNs) { super(sequenceId, snapshot); + this.timestampNs = timestampNs; } @Override public boolean equals(Object other) { if (other instanceof ActivityLaunched) { - return super.equals(other); + return timestampNs == ((ActivityLaunchFinished)other).timestampNs && + super.equals(other); } return false; } + + @Override + protected String toStringBody() { + return ", timestampNs=" + Long.toString(timestampNs); + } + + @Override + protected void writeToParcelImpl(Parcel p, int flags) { + super.writeToParcelImpl(p, flags); + p.writeLong(timestampNs); + } + + ActivityLaunchFinished(Parcel p) { + super(p); + timestampNs = p.readLong(); + } } public static class ActivityLaunchCancelled extends AppLaunchEvent { @@ -275,6 +304,42 @@ public abstract class AppLaunchEvent implements Parcelable { } } + public static final class ReportFullyDrawn extends BaseWithActivityRecordData { + public final long timestampNs; + + public ReportFullyDrawn(@SequenceId long sequenceId, + @NonNull @ActivityRecordProto byte[] snapshot, + long timestampNs) { + super(sequenceId, snapshot); + this.timestampNs = timestampNs; + } + + @Override + public boolean equals(Object other) { + if (other instanceof ReportFullyDrawn) { + return timestampNs == ((ReportFullyDrawn)other).timestampNs && + super.equals(other); + } + return false; + } + + @Override + protected String toStringBody() { + return ", timestampNs=" + Long.toString(timestampNs); + } + + @Override + protected void writeToParcelImpl(Parcel p, int flags) { + super.writeToParcelImpl(p, flags); + p.writeLong(timestampNs); + } + + ReportFullyDrawn(Parcel p) { + super(p); + timestampNs = p.readLong(); + } + } + @Override public @ContentsFlags int describeContents() { return 0; } @@ -348,6 +413,7 @@ public abstract class AppLaunchEvent implements Parcelable { ActivityLaunched.class, ActivityLaunchFinished.class, ActivityLaunchCancelled.class, + ReportFullyDrawn.class, }; public static class ActivityRecordProtoParcelable { diff --git a/startop/iorap/src/com/google/android/startop/iorap/IorapForwardingService.java b/startop/iorap/src/com/google/android/startop/iorap/IorapForwardingService.java index 902da4c0f72e..f753548516ef 100644 --- a/startop/iorap/src/com/google/android/startop/iorap/IorapForwardingService.java +++ b/startop/iorap/src/com/google/android/startop/iorap/IorapForwardingService.java @@ -315,19 +315,19 @@ public class IorapForwardingService extends SystemService { // All callbacks occur on the same background thread. Don't synchronize explicitly. @Override - public void onIntentStarted(@NonNull Intent intent) { + public void onIntentStarted(@NonNull Intent intent, long timestampNs) { // #onIntentStarted [is the only transition that] initiates a new launch sequence. ++mSequenceId; if (DEBUG) { - Log.v(TAG, String.format("AppLaunchObserver#onIntentStarted(%d, %s)", - mSequenceId, intent)); + Log.v(TAG, String.format("AppLaunchObserver#onIntentStarted(%d, %s, %d)", + mSequenceId, intent, timestampNs)); } invokeRemote(mIorapRemote, (IIorap remote) -> remote.onAppLaunchEvent(RequestId.nextValueForSequence(), - new AppLaunchEvent.IntentStarted(mSequenceId, intent)) + new AppLaunchEvent.IntentStarted(mSequenceId, intent, timestampNs)) ); } @@ -374,16 +374,34 @@ public class IorapForwardingService extends SystemService { } @Override - public void onActivityLaunchFinished(@NonNull @ActivityRecordProto byte[] activity) { + public void onActivityLaunchFinished(@NonNull @ActivityRecordProto byte[] activity, + long timestampNs) { if (DEBUG) { - Log.v(TAG, String.format("AppLaunchObserver#onActivityLaunchFinished(%d, %s)", - mSequenceId, activity)); + Log.v(TAG, String.format("AppLaunchObserver#onActivityLaunchFinished(%d, %s, %d)", + mSequenceId, activity, timestampNs)); + } + + invokeRemote(mIorapRemote, + (IIorap remote) -> + remote.onAppLaunchEvent(RequestId.nextValueForSequence(), + new AppLaunchEvent.ActivityLaunchFinished(mSequenceId, + activity, + timestampNs)) + ); + } + + @Override + public void onReportFullyDrawn(@NonNull @ActivityRecordProto byte[] activity, + long timestampNs) { + if (DEBUG) { + Log.v(TAG, String.format("AppLaunchObserver#onReportFullyDrawn(%d, %s, %d)", + mSequenceId, activity, timestampNs)); } invokeRemote(mIorapRemote, (IIorap remote) -> remote.onAppLaunchEvent(RequestId.nextValueForSequence(), - new AppLaunchEvent.ActivityLaunchFinished(mSequenceId, activity)) + new AppLaunchEvent.ReportFullyDrawn(mSequenceId, activity, timestampNs)) ); } } diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java index 1d5c18d6aae3..9e60afc7a124 100755 --- a/telephony/java/android/telephony/CarrierConfigManager.java +++ b/telephony/java/android/telephony/CarrierConfigManager.java @@ -2817,7 +2817,7 @@ public class CarrierConfigManager { "ping_test_before_data_switch_bool"; /** - * Controls time in milli seconds until DcTracker reevaluates 5G connection state. + * Controls time in milliseconds until DcTracker reevaluates 5G connection state. * @hide */ public static final String KEY_5G_WATCHDOG_TIME_MS_LONG = @@ -3199,6 +3199,13 @@ public class CarrierConfigManager { public static final String KEY_CARRIER_CERTIFICATE_STRING_ARRAY = "carrier_certificate_string_array"; + /** + * DisconnectCause array to play busy tone. Value should be array of + * {@link android.telephony.DisconnectCause}. + */ + public static final String KEY_DISCONNECT_CAUSE_PLAY_BUSYTONE_INT_ARRAY = + "disconnect_cause_play_busytone_int_array"; + /** The default value for every variable. */ private final static PersistableBundle sDefaults; @@ -3628,6 +3635,8 @@ public class CarrierConfigManager { sDefaults.putBoolean(KEY_SUPPORT_WPS_OVER_IMS_BOOL, true); sDefaults.putAll(Ims.getDefaults()); sDefaults.putStringArray(KEY_CARRIER_CERTIFICATE_STRING_ARRAY, null); + sDefaults.putIntArray(KEY_DISCONNECT_CAUSE_PLAY_BUSYTONE_INT_ARRAY, + new int[] {4 /* BUSY */}); } /** diff --git a/telephony/java/android/telephony/CellBroadcastService.java b/telephony/java/android/telephony/CellBroadcastService.java index d5e447e6c73d..46eb9df8bdad 100644 --- a/telephony/java/android/telephony/CellBroadcastService.java +++ b/telephony/java/android/telephony/CellBroadcastService.java @@ -21,6 +21,7 @@ import android.annotation.SystemApi; import android.app.Service; import android.content.Intent; import android.os.IBinder; +import android.telephony.cdma.CdmaSmsCbProgramData; /** * A service which exposes the cell broadcast handling module to the system. @@ -69,9 +70,11 @@ public abstract class CellBroadcastService extends Service { /** * Handle a CDMA cell broadcast SMS message forwarded from the system. * @param slotIndex the index of the slot which received the message - * @param message the SMS PDU + * @param bearerData the CDMA SMS bearer data + * @param serviceCategory the CDMA SCPT service category */ - public abstract void onCdmaCellBroadcastSms(int slotIndex, byte[] message); + public abstract void onCdmaCellBroadcastSms(int slotIndex, byte[] bearerData, + @CdmaSmsCbProgramData.Category int serviceCategory); /** * If overriding this method, call through to the super method for any unknown actions. @@ -102,11 +105,14 @@ public abstract class CellBroadcastService extends Service { /** * Handle a CDMA cell broadcast SMS. * @param slotIndex the index of the slot which received the broadcast - * @param message the SMS message PDU + * @param bearerData the CDMA SMS bearer data + * @param serviceCategory the CDMA SCPT service category */ @Override - public void handleCdmaCellBroadcastSms(int slotIndex, byte[] message) { - CellBroadcastService.this.onCdmaCellBroadcastSms(slotIndex, message); + public void handleCdmaCellBroadcastSms(int slotIndex, byte[] bearerData, + int serviceCategory) { + CellBroadcastService.this.onCdmaCellBroadcastSms(slotIndex, bearerData, + serviceCategory); } } } diff --git a/telephony/java/android/telephony/CellSignalStrengthWcdma.java b/telephony/java/android/telephony/CellSignalStrengthWcdma.java index cdf01dab0059..4dc54f0fef58 100644 --- a/telephony/java/android/telephony/CellSignalStrengthWcdma.java +++ b/telephony/java/android/telephony/CellSignalStrengthWcdma.java @@ -245,9 +245,12 @@ public final class CellSignalStrengthWcdma extends CellSignalStrength implements } /** - * Get the Ec/No as dB + * Get the Ec/No (Energy per chip over the noise spectral density) as dB. * - * @hide + * Reference: TS 25.133 Section 9.1.2.3 + * + * @return the Ec/No of the measured cell in the range [-24, 1] or + * {@link android.telephony.CellInfo#UNAVAILABLE UNAVAILABLE} if unavailable */ public int getEcNo() { return mEcNo; diff --git a/telephony/java/android/telephony/ICellBroadcastService.aidl b/telephony/java/android/telephony/ICellBroadcastService.aidl index eff64a2e35ba..bcd6cc546eed 100644 --- a/telephony/java/android/telephony/ICellBroadcastService.aidl +++ b/telephony/java/android/telephony/ICellBroadcastService.aidl @@ -28,5 +28,5 @@ interface ICellBroadcastService { oneway void handleGsmCellBroadcastSms(int slotId, in byte[] message); /** @see android.telephony.CellBroadcastService#onCdmaCellBroadcastSms */ - oneway void handleCdmaCellBroadcastSms(int slotId, in byte[] message); + oneway void handleCdmaCellBroadcastSms(int slotId, in byte[] bearerData, int serviceCategory); } diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java index e288f25315af..40d057ffd6fe 100644 --- a/telephony/java/android/telephony/SubscriptionManager.java +++ b/telephony/java/android/telephony/SubscriptionManager.java @@ -53,7 +53,6 @@ import android.os.ParcelUuid; import android.os.Process; import android.os.RemoteException; import android.os.ServiceManager; -import android.telephony.Annotation.NetworkType; import android.telephony.euicc.EuiccManager; import android.telephony.ims.ImsMmTelManager; import android.util.DisplayMetrics; @@ -2420,8 +2419,12 @@ public class SubscriptionManager { * @param plans the list of plans. The first plan is always the primary and * most important plan. Any additional plans are secondary and * may not be displayed or used by decision making logic. + * The list of all plans must meet the requirements defined in + * {@link SubscriptionPlan.Builder#setNetworkTypes(int[])}. * @throws SecurityException if the caller doesn't meet the requirements * outlined above. + * @throws IllegalArgumentException if plans don't meet the requirements + * mentioned above. */ public void setSubscriptionPlans(int subId, @NonNull List<SubscriptionPlan> plans) { try { @@ -2466,51 +2469,10 @@ public class SubscriptionManager { */ public void setSubscriptionOverrideUnmetered(int subId, boolean overrideUnmetered, @DurationMillisLong long timeoutMillis) { - setSubscriptionOverrideUnmetered(subId, null, overrideUnmetered, timeoutMillis); - } - - /** - * Temporarily override the billing relationship between a carrier and - * a specific subscriber to be considered unmetered for the given network - * types. This will be reflected to apps via - * {@link NetworkCapabilities#NET_CAPABILITY_NOT_METERED}. - * This method is only accessible to the following narrow set of apps: - * <ul> - * <li>The carrier app for this subscriberId, as determined by - * {@link TelephonyManager#hasCarrierPrivileges()}. - * <li>The carrier app explicitly delegated access through - * {@link CarrierConfigManager#KEY_CONFIG_PLANS_PACKAGE_OVERRIDE_STRING}. - * </ul> - * - * @param subId the subscriber this override applies to. - * @param networkTypes all network types to set an override for. A null - * network type means to apply the override to all network types. - * Any unspecified network types will default to metered. - * @param overrideUnmetered set if the billing relationship should be - * considered unmetered. - * @param timeoutMillis the timeout after which the requested override will - * be automatically cleared, or {@code 0} to leave in the - * requested state until explicitly cleared, or the next reboot, - * whichever happens first. - * @throws SecurityException if the caller doesn't meet the requirements - * outlined above. - * {@hide} - */ - public void setSubscriptionOverrideUnmetered(int subId, - @Nullable @NetworkType int[] networkTypes, boolean overrideUnmetered, - @DurationMillisLong long timeoutMillis) { try { - long networkTypeMask = 0; - if (networkTypes != null) { - for (int networkType : networkTypes) { - networkTypeMask |= TelephonyManager.getBitMaskForNetworkType(networkType); - } - } else { - networkTypeMask = ~0; - } final int overrideValue = overrideUnmetered ? OVERRIDE_UNMETERED : 0; getNetworkPolicy().setSubscriptionOverride(subId, OVERRIDE_UNMETERED, overrideValue, - networkTypeMask, timeoutMillis, mContext.getOpPackageName()); + timeoutMillis, mContext.getOpPackageName()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } @@ -2542,52 +2504,10 @@ public class SubscriptionManager { */ public void setSubscriptionOverrideCongested(int subId, boolean overrideCongested, @DurationMillisLong long timeoutMillis) { - setSubscriptionOverrideCongested(subId, null, overrideCongested, timeoutMillis); - } - - /** - * Temporarily override the billing relationship plan between a carrier and - * a specific subscriber to be considered congested. This will cause the - * device to delay certain network requests when possible, such as developer - * jobs that are willing to run in a flexible time window. - * <p> - * This method is only accessible to the following narrow set of apps: - * <ul> - * <li>The carrier app for this subscriberId, as determined by - * {@link TelephonyManager#hasCarrierPrivileges()}. - * <li>The carrier app explicitly delegated access through - * {@link CarrierConfigManager#KEY_CONFIG_PLANS_PACKAGE_OVERRIDE_STRING}. - * </ul> - * - * @param subId the subscriber this override applies to. - * @param networkTypes all network types to set an override for. A null - * network type means to apply the override to all network types. - * Any unspecified network types will default to not congested. - * @param overrideCongested set if the subscription should be considered - * congested. - * @param timeoutMillis the timeout after which the requested override will - * be automatically cleared, or {@code 0} to leave in the - * requested state until explicitly cleared, or the next reboot, - * whichever happens first. - * @throws SecurityException if the caller doesn't meet the requirements - * outlined above. - * @hide - */ - public void setSubscriptionOverrideCongested(int subId, - @Nullable @NetworkType int[] networkTypes, boolean overrideCongested, - @DurationMillisLong long timeoutMillis) { try { - long networkTypeMask = 0; - if (networkTypes != null) { - for (int networkType : networkTypes) { - networkTypeMask |= TelephonyManager.getBitMaskForNetworkType(networkType); - } - } else { - networkTypeMask = ~0; - } final int overrideValue = overrideCongested ? OVERRIDE_CONGESTED : 0; getNetworkPolicy().setSubscriptionOverride(subId, OVERRIDE_CONGESTED, overrideValue, - networkTypeMask, timeoutMillis, mContext.getOpPackageName()); + timeoutMillis, mContext.getOpPackageName()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } diff --git a/telephony/java/android/telephony/SubscriptionPlan.java b/telephony/java/android/telephony/SubscriptionPlan.java index ec2050fb1a44..e24eb2696c6c 100644 --- a/telephony/java/android/telephony/SubscriptionPlan.java +++ b/telephony/java/android/telephony/SubscriptionPlan.java @@ -24,6 +24,7 @@ import android.annotation.Nullable; import android.annotation.SystemApi; import android.os.Parcel; import android.os.Parcelable; +import android.telephony.Annotation.NetworkType; import android.util.Range; import android.util.RecurrenceRule; @@ -33,6 +34,7 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.time.Period; import java.time.ZonedDateTime; +import java.util.Arrays; import java.util.Iterator; import java.util.Objects; @@ -80,6 +82,8 @@ public final class SubscriptionPlan implements Parcelable { private int dataLimitBehavior = LIMIT_BEHAVIOR_UNKNOWN; private long dataUsageBytes = BYTES_UNKNOWN; private long dataUsageTime = TIME_UNKNOWN; + private @NetworkType int[] networkTypes; + private long networkTypesBitMask; private SubscriptionPlan(RecurrenceRule cycleRule) { this.cycleRule = Preconditions.checkNotNull(cycleRule); @@ -93,6 +97,7 @@ public final class SubscriptionPlan implements Parcelable { dataLimitBehavior = source.readInt(); dataUsageBytes = source.readLong(); dataUsageTime = source.readLong(); + networkTypes = source.createIntArray(); } @Override @@ -109,6 +114,7 @@ public final class SubscriptionPlan implements Parcelable { dest.writeInt(dataLimitBehavior); dest.writeLong(dataUsageBytes); dest.writeLong(dataUsageTime); + dest.writeIntArray(networkTypes); } @Override @@ -121,13 +127,14 @@ public final class SubscriptionPlan implements Parcelable { .append(" dataLimitBehavior=").append(dataLimitBehavior) .append(" dataUsageBytes=").append(dataUsageBytes) .append(" dataUsageTime=").append(dataUsageTime) + .append(" networkTypes=").append(Arrays.toString(networkTypes)) .append("}").toString(); } @Override public int hashCode() { return Objects.hash(cycleRule, title, summary, dataLimitBytes, dataLimitBehavior, - dataUsageBytes, dataUsageTime); + dataUsageBytes, dataUsageTime, Arrays.hashCode(networkTypes)); } @Override @@ -140,7 +147,8 @@ public final class SubscriptionPlan implements Parcelable { && dataLimitBytes == other.dataLimitBytes && dataLimitBehavior == other.dataLimitBehavior && dataUsageBytes == other.dataUsageBytes - && dataUsageTime == other.dataUsageTime; + && dataUsageTime == other.dataUsageTime + && Arrays.equals(networkTypes, other.networkTypes); } return false; } @@ -204,6 +212,32 @@ public final class SubscriptionPlan implements Parcelable { } /** + * Return an array containing all {@link NetworkType}s this SubscriptionPlan applies to. + * A null array means this SubscriptionPlan applies to all network types. + */ + public @Nullable @NetworkType int[] getNetworkTypes() { + return networkTypes; + } + + /** + * Return the networkTypes array converted to a {@link TelephonyManager.NetworkTypeBitMask} + * @hide + */ + public long getNetworkTypesBitMask() { + // calculate bitmask the first time and save for future calls + if (networkTypesBitMask == 0) { + if (networkTypes == null) { + networkTypesBitMask = ~0; + } else { + for (int networkType : networkTypes) { + networkTypesBitMask |= TelephonyManager.getBitMaskForNetworkType(networkType); + } + } + } + return networkTypesBitMask; + } + + /** * Return an iterator that will return all valid data usage cycles based on * any recurrence rules. The iterator starts from the currently active cycle * and walks backwards through time. @@ -335,5 +369,24 @@ public final class SubscriptionPlan implements Parcelable { plan.dataUsageTime = dataUsageTime; return this; } + + /** + * Set the network types this SubscriptionPlan applies to. + * The developer must supply at least one plan that applies to all network types (default), + * and all additional plans may not include a particular network type more than once. + * Plan selection will prefer plans that have specific network types defined + * over plans that apply to all network types. + * + * @param networkTypes a set of all {@link NetworkType}s that apply to this plan. + * A null value or empty array means the plan applies to all network types. + */ + public @NonNull Builder setNetworkTypes(@Nullable @NetworkType int[] networkTypes) { + if (networkTypes == null || networkTypes.length == 0) { + plan.networkTypes = null; + } else { + plan.networkTypes = networkTypes; + } + return this; + } } } diff --git a/telephony/java/android/telephony/ims/ImsReasonInfo.java b/telephony/java/android/telephony/ims/ImsReasonInfo.java index 1e0d9a786acc..10251d707c22 100644 --- a/telephony/java/android/telephony/ims/ImsReasonInfo.java +++ b/telephony/java/android/telephony/ims/ImsReasonInfo.java @@ -885,6 +885,12 @@ public final class ImsReasonInfo implements Parcelable { */ public static final int CODE_WFC_SERVICE_NOT_AVAILABLE_IN_THIS_LOCATION = 1623; + /** + * The dialed RTT call should be retried without RTT + * @hide + */ + public static final int CODE_RETRY_ON_IMS_WITHOUT_RTT = 3001; + /* * OEM specific error codes. To be used by OEMs when they don't want to reveal error code which * would be replaced by ERROR_UNSPECIFIED. @@ -1065,6 +1071,7 @@ public final class ImsReasonInfo implements Parcelable { CODE_REJECT_VT_AVPF_NOT_ALLOWED, CODE_REJECT_ONGOING_ENCRYPTED_CALL, CODE_REJECT_ONGOING_CS_CALL, + CODE_RETRY_ON_IMS_WITHOUT_RTT, CODE_OEM_CAUSE_1, CODE_OEM_CAUSE_2, CODE_OEM_CAUSE_3, diff --git a/telephony/java/com/android/internal/telephony/DctConstants.java b/telephony/java/com/android/internal/telephony/DctConstants.java index 668a6af08145..9e786ce32792 100644 --- a/telephony/java/com/android/internal/telephony/DctConstants.java +++ b/telephony/java/com/android/internal/telephony/DctConstants.java @@ -111,7 +111,7 @@ public class DctConstants { public static final int EVENT_DATA_SERVICE_BINDING_CHANGED = BASE + 49; public static final int EVENT_DEVICE_PROVISIONED_CHANGE = BASE + 50; public static final int EVENT_DATA_ENABLED_OVERRIDE_RULES_CHANGED = BASE + 51; - public static final int EVENT_5G_NETWORK_CHANGED = BASE + 52; + public static final int EVENT_SERVICE_STATE_CHANGED = BASE + 52; public static final int EVENT_5G_TIMER_HYSTERESIS = BASE + 53; public static final int EVENT_5G_TIMER_WATCHDOG = BASE + 54; diff --git a/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java b/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java index 4654437fb49c..b357fa43008f 100644 --- a/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java +++ b/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java @@ -900,6 +900,20 @@ public class SmsMessage extends SmsMessageBase { } /** + * @return the bearer data byte array + */ + public byte[] getEnvelopeBearerData() { + return mEnvelope.bearerData; + } + + /** + * @return the 16-bit CDMA SCPT service category + */ + public @CdmaSmsCbProgramData.Category int getEnvelopeServiceCategory() { + return mEnvelope.serviceCategory; + } + + /** * {@inheritDoc} */ @Override diff --git a/telephony/java/com/android/internal/telephony/cdma/sms/SmsEnvelope.java b/telephony/java/com/android/internal/telephony/cdma/sms/SmsEnvelope.java index 2787b2451470..c924ab350dc6 100644 --- a/telephony/java/com/android/internal/telephony/cdma/sms/SmsEnvelope.java +++ b/telephony/java/com/android/internal/telephony/cdma/sms/SmsEnvelope.java @@ -57,17 +57,17 @@ public final class SmsEnvelope { // CMAS alert service category assignments, see 3GPP2 C.R1001 table 9.3.3-1 public static final int SERVICE_CATEGORY_CMAS_PRESIDENTIAL_LEVEL_ALERT = - CdmaSmsCbProgramData.CATEGORY_CMAS_PRESIDENTIAL_LEVEL_ALERT; + CdmaSmsCbProgramData.CATEGORY_CMAS_PRESIDENTIAL_LEVEL_ALERT; // = 4096 public static final int SERVICE_CATEGORY_CMAS_EXTREME_THREAT = - CdmaSmsCbProgramData.CATEGORY_CMAS_EXTREME_THREAT; + CdmaSmsCbProgramData.CATEGORY_CMAS_EXTREME_THREAT; // = 4097 public static final int SERVICE_CATEGORY_CMAS_SEVERE_THREAT = - CdmaSmsCbProgramData.CATEGORY_CMAS_SEVERE_THREAT; + CdmaSmsCbProgramData.CATEGORY_CMAS_SEVERE_THREAT; // = 4098 public static final int SERVICE_CATEGORY_CMAS_CHILD_ABDUCTION_EMERGENCY = - CdmaSmsCbProgramData.CATEGORY_CMAS_CHILD_ABDUCTION_EMERGENCY; + CdmaSmsCbProgramData.CATEGORY_CMAS_CHILD_ABDUCTION_EMERGENCY; // = 4099 public static final int SERVICE_CATEGORY_CMAS_TEST_MESSAGE = - CdmaSmsCbProgramData.CATEGORY_CMAS_TEST_MESSAGE; + CdmaSmsCbProgramData.CATEGORY_CMAS_TEST_MESSAGE; // = 4100 public static final int SERVICE_CATEGORY_CMAS_LAST_RESERVED_VALUE = - CdmaSmsCbProgramData.CATEGORY_CMAS_LAST_RESERVED_VALUE; + CdmaSmsCbProgramData.CATEGORY_CMAS_LAST_RESERVED_VALUE; // = 4351 /** * Provides the type of a SMS message like point to point, broadcast or acknowledge diff --git a/test-mock/src/android/test/mock/MockContext.java b/test-mock/src/android/test/mock/MockContext.java index fcd4701c7630..5053ceedc703 100644 --- a/test-mock/src/android/test/mock/MockContext.java +++ b/test-mock/src/android/test/mock/MockContext.java @@ -758,7 +758,7 @@ public class MockContext extends Context { /** {@hide} */ @Override - public Context createContextAsUser(UserHandle user) { + public Context createContextAsUser(UserHandle user, @CreatePackageOptions int flags) { throw new UnsupportedOperationException(); } diff --git a/tests/Codegen/runTest.sh b/tests/Codegen/runTest.sh index 0e90deaadd61..929f122e261e 100755 --- a/tests/Codegen/runTest.sh +++ b/tests/Codegen/runTest.sh @@ -17,11 +17,13 @@ else header_and_eval codegen $ANDROID_BUILD_TOP/frameworks/base/tests/Codegen/src/com/android/codegentest/HierrarchicalDataClassBase.java && \ header_and_eval codegen $ANDROID_BUILD_TOP/frameworks/base/tests/Codegen/src/com/android/codegentest/HierrarchicalDataClassChild.java && \ header_and_eval codegen $ANDROID_BUILD_TOP/frameworks/base/tests/Codegen/src/com/android/codegentest/StaleDataclassDetectorFalsePositivesTest.java && \ - cd $ANDROID_BUILD_TOP && - header_and_eval mmma -j16 frameworks/base/tests/Codegen && \ - header_and_eval adb install -r -t "$(find $ANDROID_TARGET_OUT_TESTCASES -name 'CodegenTests.apk')" && \ - # header_and_eval adb shell am set-debug-app -w com.android.codegentest && \ - header_and_eval adb shell am instrument -w -e package com.android.codegentest com.android.codegentest/androidx.test.runner.AndroidJUnitRunner + ( + cd $ANDROID_BUILD_TOP && + header_and_eval mmma -j16 frameworks/base/tests/Codegen && \ + header_and_eval adb install -r -t "$(find $ANDROID_TARGET_OUT_TESTCASES -name 'CodegenTests.apk')" && \ + # header_and_eval adb shell am set-debug-app -w com.android.codegentest && \ + header_and_eval adb shell am instrument -w -e package com.android.codegentest com.android.codegentest/androidx.test.runner.AndroidJUnitRunner + ) exitCode=$? diff --git a/tests/Codegen/src/com/android/codegentest/HierrarchicalDataClassBase.java b/tests/Codegen/src/com/android/codegentest/HierrarchicalDataClassBase.java index 10eba6a899ad..35150538d3d1 100644 --- a/tests/Codegen/src/com/android/codegentest/HierrarchicalDataClassBase.java +++ b/tests/Codegen/src/com/android/codegentest/HierrarchicalDataClassBase.java @@ -32,13 +32,17 @@ public class HierrarchicalDataClassBase implements Parcelable { - // Code below generated by codegen v1.0.7. + // Code below generated by codegen v1.0.8. // // DO NOT MODIFY! // CHECKSTYLE:OFF Generated code // // To regenerate run: // $ codegen $ANDROID_BUILD_TOP/frameworks/base/tests/Codegen/src/com/android/codegentest/HierrarchicalDataClassBase.java + // + // To exclude the generated code from IntelliJ auto-formatting enable (one-time): + // Settings > Editor > Code Style > Formatter Control + //@formatter:off @DataClass.Generated.Member @@ -94,8 +98,8 @@ public class HierrarchicalDataClassBase implements Parcelable { }; @DataClass.Generated( - time = 1570576455287L, - codegenVersion = "1.0.7", + time = 1570828332402L, + codegenVersion = "1.0.8", sourceFile = "frameworks/base/tests/Codegen/src/com/android/codegentest/HierrarchicalDataClassBase.java", inputSignatures = "private int mBaseData\nclass HierrarchicalDataClassBase extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genConstructor=false, genSetters=true)") @Deprecated diff --git a/tests/Codegen/src/com/android/codegentest/HierrarchicalDataClassChild.java b/tests/Codegen/src/com/android/codegentest/HierrarchicalDataClassChild.java index 1085a6a1636a..c86740926813 100644 --- a/tests/Codegen/src/com/android/codegentest/HierrarchicalDataClassChild.java +++ b/tests/Codegen/src/com/android/codegentest/HierrarchicalDataClassChild.java @@ -46,13 +46,17 @@ public class HierrarchicalDataClassChild extends HierrarchicalDataClassBase { - // Code below generated by codegen v1.0.7. + // Code below generated by codegen v1.0.8. // // DO NOT MODIFY! // CHECKSTYLE:OFF Generated code // // To regenerate run: // $ codegen $ANDROID_BUILD_TOP/frameworks/base/tests/Codegen/src/com/android/codegentest/HierrarchicalDataClassChild.java + // + // To exclude the generated code from IntelliJ auto-formatting enable (one-time): + // Settings > Editor > Code Style > Formatter Control + //@formatter:off @DataClass.Generated.Member @@ -116,8 +120,8 @@ public class HierrarchicalDataClassChild extends HierrarchicalDataClassBase { }; @DataClass.Generated( - time = 1570576456245L, - codegenVersion = "1.0.7", + time = 1570828333399L, + codegenVersion = "1.0.8", sourceFile = "frameworks/base/tests/Codegen/src/com/android/codegentest/HierrarchicalDataClassChild.java", inputSignatures = "private @android.annotation.NonNull java.lang.String mChildData\nclass HierrarchicalDataClassChild extends com.android.codegentest.HierrarchicalDataClassBase implements []\n@com.android.internal.util.DataClass(genParcelable=true, genConstructor=false, genSetters=true)") @Deprecated diff --git a/tests/Codegen/src/com/android/codegentest/ParcelAllTheThingsDataClass.java b/tests/Codegen/src/com/android/codegentest/ParcelAllTheThingsDataClass.java index 75ef963c7995..8d097a0c978e 100644 --- a/tests/Codegen/src/com/android/codegentest/ParcelAllTheThingsDataClass.java +++ b/tests/Codegen/src/com/android/codegentest/ParcelAllTheThingsDataClass.java @@ -16,6 +16,7 @@ package com.android.codegentest; import android.annotation.NonNull; +import android.annotation.Nullable; import android.os.Parcel; import android.os.Parcelable; import android.util.SparseArray; @@ -46,15 +47,22 @@ public class ParcelAllTheThingsDataClass implements Parcelable { @NonNull SparseArray<SampleWithCustomBuilder> mSparseArray = null; @NonNull SparseIntArray mSparseIntArray = null; + @SuppressWarnings({"WeakerAccess"}) + @Nullable Boolean mNullableBoolean = null; - // Code below generated by codegen v1.0.7. + + // Code below generated by codegen v1.0.8. // // DO NOT MODIFY! // CHECKSTYLE:OFF Generated code // // To regenerate run: // $ codegen $ANDROID_BUILD_TOP/frameworks/base/tests/Codegen/src/com/android/codegentest/ParcelAllTheThingsDataClass.java + // + // To exclude the generated code from IntelliJ auto-formatting enable (one-time): + // Settings > Editor > Code Style > Formatter Control + //@formatter:off @DataClass.Generated.Member @@ -65,7 +73,8 @@ public class ParcelAllTheThingsDataClass implements Parcelable { @NonNull Map<String,SampleWithCustomBuilder> map, @NonNull Map<String,String> stringMap, @NonNull SparseArray<SampleWithCustomBuilder> sparseArray, - @NonNull SparseIntArray sparseIntArray) { + @NonNull SparseIntArray sparseIntArray, + @SuppressWarnings({ "WeakerAccess" }) @Nullable Boolean nullableBoolean) { this.mStringArray = stringArray; AnnotationValidations.validate( NonNull.class, null, mStringArray); @@ -87,6 +96,7 @@ public class ParcelAllTheThingsDataClass implements Parcelable { this.mSparseIntArray = sparseIntArray; AnnotationValidations.validate( NonNull.class, null, mSparseIntArray); + this.mNullableBoolean = nullableBoolean; // onConstructed(); // You can define this method to get a callback } @@ -126,6 +136,11 @@ public class ParcelAllTheThingsDataClass implements Parcelable { return mSparseIntArray; } + @DataClass.Generated.Member + public @SuppressWarnings({ "WeakerAccess" }) @Nullable Boolean getNullableBoolean() { + return mNullableBoolean; + } + @Override @DataClass.Generated.Member public String toString() { @@ -139,7 +154,8 @@ public class ParcelAllTheThingsDataClass implements Parcelable { "map = " + mMap + ", " + "stringMap = " + mStringMap + ", " + "sparseArray = " + mSparseArray + ", " + - "sparseIntArray = " + mSparseIntArray + + "sparseIntArray = " + mSparseIntArray + ", " + + "nullableBoolean = " + mNullableBoolean + " }"; } @@ -149,6 +165,9 @@ public class ParcelAllTheThingsDataClass implements Parcelable { // You can override field parcelling by defining methods like: // void parcelFieldName(Parcel dest, int flags) { ... } + int flg = 0; + if (mNullableBoolean != null) flg |= 0x80; + dest.writeInt(flg); dest.writeStringArray(mStringArray); dest.writeIntArray(mIntArray); dest.writeStringList(mStringList); @@ -156,6 +175,7 @@ public class ParcelAllTheThingsDataClass implements Parcelable { dest.writeMap(mStringMap); dest.writeSparseArray(mSparseArray); dest.writeSparseIntArray(mSparseIntArray); + if (mNullableBoolean != null) dest.writeBoolean(mNullableBoolean); } @Override @@ -169,6 +189,7 @@ public class ParcelAllTheThingsDataClass implements Parcelable { // You can override field unparcelling by defining methods like: // static FieldType unparcelFieldName(Parcel in) { ... } + int flg = in.readInt(); String[] stringArray = in.createStringArray(); int[] intArray = in.createIntArray(); List<String> stringList = new java.util.ArrayList<>(); @@ -179,6 +200,7 @@ public class ParcelAllTheThingsDataClass implements Parcelable { in.readMap(stringMap, String.class.getClassLoader()); SparseArray<SampleWithCustomBuilder> sparseArray = (SparseArray) in.readSparseArray(SampleWithCustomBuilder.class.getClassLoader()); SparseIntArray sparseIntArray = (SparseIntArray) in.readSparseIntArray(); + Boolean nullableBoolean = (flg & 0x80) == 0 ? null : (Boolean) in.readBoolean(); this.mStringArray = stringArray; AnnotationValidations.validate( @@ -201,6 +223,7 @@ public class ParcelAllTheThingsDataClass implements Parcelable { this.mSparseIntArray = sparseIntArray; AnnotationValidations.validate( NonNull.class, null, mSparseIntArray); + this.mNullableBoolean = nullableBoolean; // onConstructed(); // You can define this method to get a callback } @@ -233,6 +256,7 @@ public class ParcelAllTheThingsDataClass implements Parcelable { private @NonNull Map<String,String> mStringMap; private @NonNull SparseArray<SampleWithCustomBuilder> mSparseArray; private @NonNull SparseIntArray mSparseIntArray; + private @SuppressWarnings({ "WeakerAccess" }) @Nullable Boolean mNullableBoolean; private long mBuilderFieldsSet = 0L; @@ -328,10 +352,18 @@ public class ParcelAllTheThingsDataClass implements Parcelable { return this; } + @DataClass.Generated.Member + public @NonNull Builder setNullableBoolean(@SuppressWarnings({ "WeakerAccess" }) @Nullable Boolean value) { + checkNotUsed(); + mBuilderFieldsSet |= 0x80; + mNullableBoolean = value; + return this; + } + /** Builds the instance. This builder should not be touched after calling this! */ public ParcelAllTheThingsDataClass build() { checkNotUsed(); - mBuilderFieldsSet |= 0x80; // Mark builder used + mBuilderFieldsSet |= 0x100; // Mark builder used if ((mBuilderFieldsSet & 0x1) == 0) { mStringArray = null; @@ -354,6 +386,9 @@ public class ParcelAllTheThingsDataClass implements Parcelable { if ((mBuilderFieldsSet & 0x40) == 0) { mSparseIntArray = null; } + if ((mBuilderFieldsSet & 0x80) == 0) { + mNullableBoolean = null; + } ParcelAllTheThingsDataClass o = new ParcelAllTheThingsDataClass( mStringArray, mIntArray, @@ -361,12 +396,13 @@ public class ParcelAllTheThingsDataClass implements Parcelable { mMap, mStringMap, mSparseArray, - mSparseIntArray); + mSparseIntArray, + mNullableBoolean); return o; } private void checkNotUsed() { - if ((mBuilderFieldsSet & 0x80) != 0) { + if ((mBuilderFieldsSet & 0x100) != 0) { throw new IllegalStateException( "This Builder should not be reused. Use a new Builder instance instead"); } @@ -374,10 +410,10 @@ public class ParcelAllTheThingsDataClass implements Parcelable { } @DataClass.Generated( - time = 1570576454326L, - codegenVersion = "1.0.7", + time = 1570828331396L, + codegenVersion = "1.0.8", sourceFile = "frameworks/base/tests/Codegen/src/com/android/codegentest/ParcelAllTheThingsDataClass.java", - inputSignatures = " @android.annotation.NonNull java.lang.String[] mStringArray\n @android.annotation.NonNull int[] mIntArray\n @android.annotation.NonNull java.util.List<java.lang.String> mStringList\n @android.annotation.NonNull java.util.Map<java.lang.String,com.android.codegentest.SampleWithCustomBuilder> mMap\n @android.annotation.NonNull java.util.Map<java.lang.String,java.lang.String> mStringMap\n @android.annotation.NonNull android.util.SparseArray<com.android.codegentest.SampleWithCustomBuilder> mSparseArray\n @android.annotation.NonNull android.util.SparseIntArray mSparseIntArray\nclass ParcelAllTheThingsDataClass extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genBuilder=true, genAidl=false, genToString=true)") + inputSignatures = " @android.annotation.NonNull java.lang.String[] mStringArray\n @android.annotation.NonNull int[] mIntArray\n @android.annotation.NonNull java.util.List<java.lang.String> mStringList\n @android.annotation.NonNull java.util.Map<java.lang.String,com.android.codegentest.SampleWithCustomBuilder> mMap\n @android.annotation.NonNull java.util.Map<java.lang.String,java.lang.String> mStringMap\n @android.annotation.NonNull android.util.SparseArray<com.android.codegentest.SampleWithCustomBuilder> mSparseArray\n @android.annotation.NonNull android.util.SparseIntArray mSparseIntArray\n @java.lang.SuppressWarnings({\"WeakerAccess\"}) @android.annotation.Nullable java.lang.Boolean mNullableBoolean\nclass ParcelAllTheThingsDataClass extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genBuilder=true, genAidl=false, genToString=true)") @Deprecated private void __metadata() {} diff --git a/tests/Codegen/src/com/android/codegentest/SampleDataClass.java b/tests/Codegen/src/com/android/codegentest/SampleDataClass.java index 14010a9cfb1d..d014d6d27ce8 100644 --- a/tests/Codegen/src/com/android/codegentest/SampleDataClass.java +++ b/tests/Codegen/src/com/android/codegentest/SampleDataClass.java @@ -342,13 +342,17 @@ public final class SampleDataClass implements Parcelable { - // Code below generated by codegen v1.0.7. + // Code below generated by codegen v1.0.8. // // DO NOT MODIFY! // CHECKSTYLE:OFF Generated code // // To regenerate run: // $ codegen $ANDROID_BUILD_TOP/frameworks/base/tests/Codegen/src/com/android/codegentest/SampleDataClass.java + // + // To exclude the generated code from IntelliJ auto-formatting enable (one-time): + // Settings > Editor > Code Style > Formatter Control + //@formatter:off @IntDef(prefix = "STATE_", value = { @@ -1868,8 +1872,8 @@ public final class SampleDataClass implements Parcelable { } @DataClass.Generated( - time = 1570576452225L, - codegenVersion = "1.0.7", + time = 1570828329319L, + codegenVersion = "1.0.8", sourceFile = "frameworks/base/tests/Codegen/src/com/android/codegentest/SampleDataClass.java", inputSignatures = "public static final java.lang.String STATE_NAME_UNDEFINED\npublic static final java.lang.String STATE_NAME_ON\npublic static final java.lang.String STATE_NAME_OFF\npublic static final int STATE_UNDEFINED\npublic static final int STATE_ON\npublic static final int STATE_OFF\npublic static final @com.android.codegentest.SampleDataClass.RequestFlags int FLAG_MANUAL_REQUEST\npublic static final @com.android.codegentest.SampleDataClass.RequestFlags int FLAG_COMPATIBILITY_MODE_REQUEST\npublic static final @com.android.codegentest.SampleDataClass.RequestFlags int FLAG_AUGMENTED_REQUEST\nprivate int mNum\nprivate int mNum2\nprivate int mNum4\nprivate @android.annotation.Nullable java.lang.String mName\nprivate @android.annotation.NonNull java.lang.String mName2\nprivate @android.annotation.NonNull java.lang.String mName4\nprivate @android.annotation.Nullable android.view.accessibility.AccessibilityNodeInfo mOtherParcelable\nprivate @com.android.internal.util.DataClass.ParcelWith(com.android.codegentest.MyDateParcelling.class) @android.annotation.NonNull java.util.Date mDate\nprivate @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForPattern.class) @android.annotation.NonNull java.util.regex.Pattern mPattern\nprivate @android.annotation.NonNull java.util.List<android.net.LinkAddress> mLinkAddresses2\nprivate @com.android.internal.util.DataClass.PluralOf(\"linkAddress\") @android.annotation.NonNull java.util.ArrayList<android.net.LinkAddress> mLinkAddresses\nprivate @android.annotation.Nullable android.net.LinkAddress[] mLinkAddresses4\nprivate @com.android.codegentest.SampleDataClass.StateName @android.annotation.NonNull java.lang.String mStateName\nprivate @com.android.codegentest.SampleDataClass.RequestFlags int mFlags\nprivate @com.android.codegentest.SampleDataClass.State int mState\npublic @android.annotation.NonNull java.lang.CharSequence charSeq\nprivate final @android.annotation.Nullable android.net.LinkAddress[] mLinkAddresses5\nprivate transient android.net.LinkAddress[] mLinkAddresses6\ntransient int[] mTmpStorage\nprivate @android.annotation.StringRes int mStringRes\nprivate @android.annotation.IntRange(from=0L, to=6L) int mDayOfWeek\nprivate @android.annotation.Size(2L) @android.annotation.NonNull @com.android.internal.util.DataClass.Each @android.annotation.FloatRange(from=0.0) float[] mCoords\nprivate static java.lang.String defaultName4()\nprivate int[] lazyInitTmpStorage()\npublic android.net.LinkAddress[] getLinkAddresses4()\nprivate boolean patternEquals(java.util.regex.Pattern)\nprivate int patternHashCode()\nprivate void onConstructed()\npublic void dump(java.io.PrintWriter)\nclass SampleDataClass extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genBuilder=true, genConstructor=true, genEqualsHashCode=true, genToString=true, genForEachField=true, genSetters=true)") @Deprecated diff --git a/tests/Codegen/src/com/android/codegentest/SampleWithCustomBuilder.java b/tests/Codegen/src/com/android/codegentest/SampleWithCustomBuilder.java index b5f6c73a8aef..1c87e8f0ab36 100644 --- a/tests/Codegen/src/com/android/codegentest/SampleWithCustomBuilder.java +++ b/tests/Codegen/src/com/android/codegentest/SampleWithCustomBuilder.java @@ -85,13 +85,17 @@ public class SampleWithCustomBuilder implements Parcelable { - // Code below generated by codegen v1.0.7. + // Code below generated by codegen v1.0.8. // // DO NOT MODIFY! // CHECKSTYLE:OFF Generated code // // To regenerate run: // $ codegen $ANDROID_BUILD_TOP/frameworks/base/tests/Codegen/src/com/android/codegentest/SampleWithCustomBuilder.java + // + // To exclude the generated code from IntelliJ auto-formatting enable (one-time): + // Settings > Editor > Code Style > Formatter Control + //@formatter:off @DataClass.Generated.Member @@ -249,8 +253,8 @@ public class SampleWithCustomBuilder implements Parcelable { } @DataClass.Generated( - time = 1570576453295L, - codegenVersion = "1.0.7", + time = 1570828330331L, + codegenVersion = "1.0.8", sourceFile = "frameworks/base/tests/Codegen/src/com/android/codegentest/SampleWithCustomBuilder.java", inputSignatures = " long delayAmount\n @android.annotation.NonNull java.util.concurrent.TimeUnit delayUnit\n long creationTimestamp\nprivate static java.util.concurrent.TimeUnit unparcelDelayUnit(android.os.Parcel)\nprivate void parcelDelayUnit(android.os.Parcel,int)\nclass SampleWithCustomBuilder extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genBuilder=true, genAidl=false, genToString=true)\nabstract com.android.codegentest.SampleWithCustomBuilder.Builder setDelayAmount(long)\npublic abstract com.android.codegentest.SampleWithCustomBuilder.Builder setDelayUnit(java.util.concurrent.TimeUnit)\npublic com.android.codegentest.SampleWithCustomBuilder.Builder setDelay(long,java.util.concurrent.TimeUnit)\nclass BaseBuilder extends java.lang.Object implements []") @Deprecated diff --git a/tests/Codegen/src/com/android/codegentest/StaleDataclassDetectorFalsePositivesTest.java b/tests/Codegen/src/com/android/codegentest/StaleDataclassDetectorFalsePositivesTest.java index 0ce8aba9b28f..27af37fe23ab 100644 --- a/tests/Codegen/src/com/android/codegentest/StaleDataclassDetectorFalsePositivesTest.java +++ b/tests/Codegen/src/com/android/codegentest/StaleDataclassDetectorFalsePositivesTest.java @@ -51,18 +51,22 @@ public class StaleDataclassDetectorFalsePositivesTest { - // Code below generated by codegen v1.0.7. + // Code below generated by codegen v1.0.8. // // DO NOT MODIFY! // CHECKSTYLE:OFF Generated code // // To regenerate run: // $ codegen $ANDROID_BUILD_TOP/frameworks/base/tests/Codegen/src/com/android/codegentest/StaleDataclassDetectorFalsePositivesTest.java + // + // To exclude the generated code from IntelliJ auto-formatting enable (one-time): + // Settings > Editor > Code Style > Formatter Control + //@formatter:off @DataClass.Generated( - time = 1570576457249L, - codegenVersion = "1.0.7", + time = 1570828334384L, + codegenVersion = "1.0.8", sourceFile = "frameworks/base/tests/Codegen/src/com/android/codegentest/StaleDataclassDetectorFalsePositivesTest.java", inputSignatures = "public @android.annotation.NonNull java.lang.String someMethod(int)\nclass StaleDataclassDetectorFalsePositivesTest extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genConstructor=false, genBuilder=false)") @Deprecated diff --git a/tests/Gating/Android.bp b/tests/Gating/Android.bp new file mode 100644 index 000000000000..b6c00948f5f3 --- /dev/null +++ b/tests/Gating/Android.bp @@ -0,0 +1,17 @@ +android_test { + name: "Gating", + // Only compile source java files in this apk. + srcs: ["src/**/*.java"], + certificate: "platform", + libs: [ + "android.test.runner", + "android.test.base", + ], + static_libs: [ + "junit", + "android-support-test", + "mockito-target-minus-junit4", + "truth-prebuilt", + "platform_compat-test-rules" + ], +} diff --git a/tests/Gating/AndroidManifest.xml b/tests/Gating/AndroidManifest.xml new file mode 100644 index 000000000000..7f14b83fbc75 --- /dev/null +++ b/tests/Gating/AndroidManifest.xml @@ -0,0 +1,11 @@ +<?xml version="1.0" encoding="utf-8"?> + +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.tests.gating"> + <application android:label="GatingTest"> + <uses-library android:name="android.test.runner" /> + </application> + + <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner" + android:targetPackage="com.android.tests.gating"/> +</manifest> diff --git a/tests/Gating/AndroidTest.xml b/tests/Gating/AndroidTest.xml new file mode 100644 index 000000000000..730e74a6d337 --- /dev/null +++ b/tests/Gating/AndroidTest.xml @@ -0,0 +1,30 @@ +<!-- 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. +--> +<configuration description="Test compatibility change gating."> + <target_preparer class="com.android.tradefed.targetprep.TestFilePushSetup"/> + <target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup"> + <option name="test-file-name" value="Gating.apk"/> + </target_preparer> + <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer"/> + <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer"/> + <option name="test-suite-tag" value="apct"/> + <option name="test-tag" value="Gating"/> + + <test class="com.android.tradefed.testtype.AndroidJUnitTest"> + <option name="package" value="com.android.tests.gating"/> + <option name="runner" value="android.support.test.runner.AndroidJUnitRunner"/> + <option name="hidden-api-checks" value="false"/> + </test> +</configuration> diff --git a/tests/Gating/src/com/android/compat/DummyApi.java b/tests/Gating/src/com/android/compat/DummyApi.java new file mode 100644 index 000000000000..93d60d07e63e --- /dev/null +++ b/tests/Gating/src/com/android/compat/DummyApi.java @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2016 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.compat; + +import android.compat.Compatibility; +import android.content.Context; +import android.os.RemoteException; +import android.os.ServiceManager; + +import com.android.internal.compat.IPlatformCompat; + +/** + * This is a dummy API to test gating + * + * @hide + */ +public class DummyApi { + + public static final long CHANGE_ID = 666013; + public static final long CHANGE_ID_1 = 666014; + public static final long CHANGE_ID_2 = 666015; + public static final long CHANGE_SYSTEM_SERVER = 666016; + + /** + * Dummy method + * @return "A" if change is enabled, "B" otherwise. + */ + public static String dummyFunc() { + if (Compatibility.isChangeEnabled(CHANGE_ID)) { + return "A"; + } + return "B"; + } + + /** + * Dummy combined method + * @return "0" if {@link CHANGE_ID_1} is disabled and {@link CHANGE_ID_2} is disabled, + "1" if {@link CHANGE_ID_1} is disabled and {@link CHANGE_ID_2} is enabled, + "2" if {@link CHANGE_ID_1} is enabled and {@link CHANGE_ID_2} is disabled, + "3" if {@link CHANGE_ID_1} is enabled and {@link CHANGE_ID_2} is enabled. + */ + public static String dummyCombinedFunc() { + if (!Compatibility.isChangeEnabled(CHANGE_ID_1) + && !Compatibility.isChangeEnabled(CHANGE_ID_2)) { + return "0"; + } else if (!Compatibility.isChangeEnabled(CHANGE_ID_1) + && Compatibility.isChangeEnabled(CHANGE_ID_2)) { + return "1"; + } else if (Compatibility.isChangeEnabled(CHANGE_ID_1) + && !Compatibility.isChangeEnabled(CHANGE_ID_2)) { + return "2"; + } + return "3"; + } + + /** + * Dummy api using system server API. + */ + public static String dummySystemServer(Context context) { + IPlatformCompat platformCompat = IPlatformCompat.Stub + .asInterface(ServiceManager.getService(Context.PLATFORM_COMPAT_SERVICE)); + if (platformCompat == null) { + throw new RuntimeException("Could not obtain IPlatformCompat instance!"); + } + String packageName = context.getPackageName(); + try { + if (platformCompat.isChangeEnabledByPackageName(CHANGE_SYSTEM_SERVER, packageName)) { + return "True"; + } else { + return "False"; + } + } catch (RemoteException e) { + throw new RuntimeException("Could not get change value!", e); + } + } +} diff --git a/tests/Gating/src/com/android/tests/gating/GatingTest.java b/tests/Gating/src/com/android/tests/gating/GatingTest.java new file mode 100644 index 000000000000..08676242b1bd --- /dev/null +++ b/tests/Gating/src/com/android/tests/gating/GatingTest.java @@ -0,0 +1,95 @@ +/* + * 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.tests.gating; + +import static com.google.common.truth.Truth.assertThat; + +import android.compat.CompatChangeRule; +import android.compat.CompatChangeRule.DisableCompatChanges; +import android.compat.CompatChangeRule.EnableCompatChanges; +import android.support.test.InstrumentationRegistry; +import android.support.test.runner.AndroidJUnit4; + +import com.android.compat.DummyApi; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TestRule; +import org.junit.runner.RunWith; + +/** + * Tests for platform compatibility change gating. + */ +@RunWith(AndroidJUnit4.class) +public class GatingTest { + + @Rule + public TestRule compatChangeRule = new CompatChangeRule(); + + @Test + @EnableCompatChanges({DummyApi.CHANGE_ID}) + public void testDummyGatingPositive() { + assertThat(DummyApi.dummyFunc()).isEqualTo("A"); + } + + @Test + @DisableCompatChanges({DummyApi.CHANGE_ID}) + public void testDummyGatingNegative() { + assertThat(DummyApi.dummyFunc()).isEqualTo("B"); + } + + @Test + @DisableCompatChanges({DummyApi.CHANGE_ID_1, DummyApi.CHANGE_ID_2}) + public void testDummyGatingCombined0() { + assertThat(DummyApi.dummyCombinedFunc()).isEqualTo("0"); + } + + @Test + @DisableCompatChanges({DummyApi.CHANGE_ID_1}) + @EnableCompatChanges({DummyApi.CHANGE_ID_2}) + public void testDummyGatingCombined1() { + assertThat(DummyApi.dummyCombinedFunc()).isEqualTo("1"); + } + + @Test + @EnableCompatChanges({DummyApi.CHANGE_ID_1}) + @DisableCompatChanges({DummyApi.CHANGE_ID_2}) + public void testDummyGatingCombined2() { + assertThat(DummyApi.dummyCombinedFunc()).isEqualTo("2"); + } + + @Test + @EnableCompatChanges({DummyApi.CHANGE_ID_1, DummyApi.CHANGE_ID_2}) + public void testDummyGatingCombined3() { + assertThat(DummyApi.dummyCombinedFunc()).isEqualTo("3"); + } + + @Test + @EnableCompatChanges({DummyApi.CHANGE_SYSTEM_SERVER}) + public void testDummyGatingPositiveSystemServer() { + assertThat( + DummyApi.dummySystemServer(InstrumentationRegistry.getTargetContext())).isEqualTo( + "True"); + } + + @Test + @DisableCompatChanges({DummyApi.CHANGE_SYSTEM_SERVER}) + public void testDummyGatingNegativeSystemServer() { + assertThat( + DummyApi.dummySystemServer(InstrumentationRegistry.getTargetContext())).isEqualTo( + "False"); + } +} diff --git a/tests/RollbackTest/Android.bp b/tests/RollbackTest/Android.bp index 231d045bd817..085c53cf45ea 100644 --- a/tests/RollbackTest/Android.bp +++ b/tests/RollbackTest/Android.bp @@ -19,7 +19,6 @@ android_test { static_libs: ["androidx.test.rules", "cts-rollback-lib", "cts-install-lib"], test_suites: ["general-tests"], test_config: "RollbackTest.xml", - // TODO: sdk_version: "test_current" when Intent#resolveSystemservice is TestApi } java_test_host { diff --git a/tools/codegen/src/com/android/codegen/FieldInfo.kt b/tools/codegen/src/com/android/codegen/FieldInfo.kt index ba00264f5f5e..1a7fd6e241aa 100644 --- a/tools/codegen/src/com/android/codegen/FieldInfo.kt +++ b/tools/codegen/src/com/android/codegen/FieldInfo.kt @@ -191,7 +191,7 @@ data class FieldInfo( * Parcel.write* and Parcel.read* method name wildcard values */ val ParcelMethodsSuffix = when { - FieldClass in PRIMITIVE_TYPES - "char" - "boolean" + + FieldClass in PRIMITIVE_TYPES - "char" - "boolean" + BOXED_PRIMITIVE_TYPES + listOf("String", "CharSequence", "Exception", "Size", "SizeF", "Bundle", "FileDescriptor", "SparseBooleanArray", "SparseIntArray", "SparseArray") -> FieldClass diff --git a/tools/codegen/src/com/android/codegen/Generators.kt b/tools/codegen/src/com/android/codegen/Generators.kt index 5a95676c1dc8..0ebb3cf2a447 100644 --- a/tools/codegen/src/com/android/codegen/Generators.kt +++ b/tools/codegen/src/com/android/codegen/Generators.kt @@ -854,6 +854,7 @@ private fun ClassPrinter.generateFieldValidation(field: FieldInfo) = field.run { it.nameAsString == intOrStringDef?.AnnotationName || it.nameAsString in knownNonValidationAnnotations || it in perElementValidations + || it.args.any { (_, value) -> value is ArrayInitializerExpr } }.forEach { annotation -> appendValidateCall(annotation, valueToValidate = if (annotation.nameAsString == Size) sizeExpr else name) @@ -874,14 +875,7 @@ fun ClassPrinter.appendValidateCall(annotation: AnnotationExpr, valueToValidate: val validate = memberRef("com.android.internal.util.AnnotationValidations.validate") "$validate(" { !"${annotation.nameAsString}.class, null, $valueToValidate" - val params = when (annotation) { - is MarkerAnnotationExpr -> emptyMap() - is SingleMemberAnnotationExpr -> mapOf("value" to annotation.memberValue) - is NormalAnnotationExpr -> - annotation.pairs.map { it.name.asString() to it.value }.toMap() - else -> throw IllegalStateException() - } - params.forEach { name, value -> + annotation.args.forEach { name, value -> !",\n\"$name\", $value" } } diff --git a/tools/codegen/src/com/android/codegen/InputSignaturesComputation.kt b/tools/codegen/src/com/android/codegen/InputSignaturesComputation.kt index 24cf4690dae1..d6953c00fc0b 100644 --- a/tools/codegen/src/com/android/codegen/InputSignaturesComputation.kt +++ b/tools/codegen/src/com/android/codegen/InputSignaturesComputation.kt @@ -87,6 +87,14 @@ private fun ClassPrinter.appendExpr(sb: StringBuilder, ex: Expression?) { is IntegerLiteralExpr -> sb.append(ex.asInt()).append("L") is LongLiteralExpr -> sb.append(ex.asLong()).append("L") is DoubleLiteralExpr -> sb.append(ex.asDouble()) + is ArrayInitializerExpr -> { + sb.append("{") + ex.values.forEachLastAware { arrayElem, isLast -> + appendExpr(sb, arrayElem) + if (!isLast) sb.append(", ") + } + sb.append("}") + } else -> sb.append(ex) } } diff --git a/tools/codegen/src/com/android/codegen/Main.kt b/tools/codegen/src/com/android/codegen/Main.kt index 039f7b2fc627..ce83d3dc8e51 100755 --- a/tools/codegen/src/com/android/codegen/Main.kt +++ b/tools/codegen/src/com/android/codegen/Main.kt @@ -9,6 +9,7 @@ const val GENERATED_WARNING_PREFIX = "Code below generated by $CODEGEN_NAME" const val INDENT_SINGLE = " " val PRIMITIVE_TYPES = listOf("byte", "short", "int", "long", "char", "float", "double", "boolean") +val BOXED_PRIMITIVE_TYPES = PRIMITIVE_TYPES.map { it.capitalize() } - "Int" + "Integer" - "Char" + "Character" val BUILTIN_SPECIAL_PARCELLINGS = listOf("Pattern") @@ -142,6 +143,10 @@ fun main(args: Array<String>) { // // To regenerate run: // $ $cliExecutable ${cliArgs.dropLast(1).joinToString("") { "$it " }}$fileEscaped + // + // To exclude the generated code from IntelliJ auto-formatting enable (one-time): + // Settings > Editor > Code Style > Formatter Control + //@formatter:off """ diff --git a/tools/codegen/src/com/android/codegen/SharedConstants.kt b/tools/codegen/src/com/android/codegen/SharedConstants.kt index 47f774f07f16..8c4583f68c4b 100644 --- a/tools/codegen/src/com/android/codegen/SharedConstants.kt +++ b/tools/codegen/src/com/android/codegen/SharedConstants.kt @@ -1,7 +1,7 @@ package com.android.codegen const val CODEGEN_NAME = "codegen" -const val CODEGEN_VERSION = "1.0.7" +const val CODEGEN_VERSION = "1.0.8" const val CANONICAL_BUILDER_CLASS = "Builder" const val BASE_BUILDER_CLASS = "BaseBuilder" diff --git a/tools/codegen/src/com/android/codegen/Utils.kt b/tools/codegen/src/com/android/codegen/Utils.kt index a1f068afa29a..e703397214eb 100644 --- a/tools/codegen/src/com/android/codegen/Utils.kt +++ b/tools/codegen/src/com/android/codegen/Utils.kt @@ -1,9 +1,6 @@ package com.android.codegen -import com.github.javaparser.ast.Modifier -import com.github.javaparser.ast.expr.AnnotationExpr -import com.github.javaparser.ast.expr.Expression -import com.github.javaparser.ast.expr.SingleMemberAnnotationExpr +import com.github.javaparser.ast.expr.* import com.github.javaparser.ast.nodeTypes.NodeWithModifiers import java.time.Instant import java.time.ZoneId @@ -88,3 +85,10 @@ fun abort(msg: String): Nothing { } fun bitAtExpr(bitIndex: Int) = "0x${java.lang.Long.toHexString(1L shl bitIndex)}" + +val AnnotationExpr.args: Map<String, Expression> get() = when (this) { + is MarkerAnnotationExpr -> emptyMap() + is SingleMemberAnnotationExpr -> mapOf("value" to memberValue) + is NormalAnnotationExpr -> pairs.map { it.name.asString() to it.value }.toMap() + else -> throw IllegalArgumentException("Unknown annotation expression: $this") +} diff --git a/tools/streaming_proto/Android.bp b/tools/streaming_proto/Android.bp index 14eead853f50..1390f63248f9 100644 --- a/tools/streaming_proto/Android.bp +++ b/tools/streaming_proto/Android.bp @@ -29,7 +29,7 @@ cc_defaults { "-Werror", ], - shared_libs: ["libprotoc"], + static_libs: ["libprotoc"], } cc_binary_host { |